A framework-agnostic, OCI-based registry for AI agent skills.
Packages skills as standard OCI images, manages their lifecycle
(draft, testing, published, deprecated, archived), and works with
the container tools enterprises already use: podman, skopeo,
crane, and Kubernetes ImageVolumes.
Companion to agentoperations/agent-registry (metadata and governance layer).
Skills are packaged as standard OCI images (FROM scratch), not
ORAS artifacts. This means:
podman pull/skopeo copy/crane pullwork out of the box- Kubernetes ImageVolumes can mount skills directly into agent pods
- Standard registries (Quay, GHCR, Zot) index and serve them normally
- Signing and verification via cosign/sigstore (planned)
brew install pavelanni/tap/skillctlcurl -fsSL https://raw.githubusercontent.com/redhat-et/skillimage/main/install.sh | shTo install a specific version or to a custom directory:
curl -fsSL https://raw.githubusercontent.com/redhat-et/skillimage/main/install.sh \
| VERSION=0.1.0 INSTALL_DIR=~/.local/bin shgo install github.com/redhat-et/skillimage/cmd/skillctl@latestpodman run --rm ghcr.io/redhat-et/skillctl:latest versionmake build # produces bin/skillctlPre-built binaries for Linux, macOS, and Windows (amd64/arm64) are available on the releases page.
A skill is a directory with a skill.yaml (SkillCard metadata)
and a SKILL.md (prompt content). See examples/hello-world/
for a working example.
apiVersion: skillimage.io/v1alpha1
kind: SkillCard
metadata:
name: hello-world
namespace: examples
version: 1.0.0
description: A simple example skill.
spec:
prompt: SKILL.mdThe namespace field groups skills locally. When you run
skillctl list, skills display as namespace/name (e.g.,
examples/hello-world). The namespace is a logical grouping
within the skill card and is independent of the remote registry
path.
# Validate the SkillCard against the JSON Schema
bin/skillctl validate examples/hello-world/
# Pack into a local OCI image (tagged as 1.0.0-draft)
bin/skillctl pack examples/hello-world/
# List local images
bin/skillctl list
# Inspect metadata and OCI details
bin/skillctl inspect examples/hello-world:1.0.0-draft# Promote draft -> testing (retagged as 1.0.0-testing)
bin/skillctl promote examples/hello-world:1.0.0-draft --to testing --local
# Promote testing -> published (retagged as 1.0.0 + latest)
bin/skillctl promote examples/hello-world:1.0.0-testing --to published --localPromotion updates OCI manifest annotations and retags without modifying image content. The layer digest stays the same from draft through published.
# Push to a remote registry
bin/skillctl push quay.io/myorg/hello-world:1.0.0-draft
# Pull from a remote registry
bin/skillctl pull quay.io/myorg/hello-world:1.0.0 -o ./skills/We recommend aligning the remote registry path with the skill's
namespace field. For example, a skill with namespace: business
would push to quay.io/myorg/business/hello-world:1.0.0-draft.
This is a convention, not enforced by skillctl, but it makes it
easier to find skills in both local and remote listings.
Authentication uses your existing ~/.docker/config.json or
Podman's auth.json -- no separate login needed.
Skill images are standard OCI images. You can inspect metadata from any registry without downloading the image:
# View all metadata annotations (no image download)
skopeo inspect docker://quay.io/myorg/hello-world:1.0.0
# Get just the skill tags
skopeo inspect docker://quay.io/myorg/hello-world:1.0.0 \
| jq -r '.Annotations["io.skillimage.tags"]'
# → ["example","getting-started"]
# Get lifecycle status
skopeo inspect docker://quay.io/myorg/hello-world:1.0.0 \
| jq -r '.Annotations["io.skillimage.status"]'
# → publishedThis works because all skill metadata is stored in OCI manifest annotations, not inside the image layers. A catalog UI or CI pipeline can read skill metadata with a single manifest fetch.
Since skills are standard OCI images, you can mount them directly
into running containers with --mount type=image:
podman pull quay.io/myorg/hello-world:1.0.0
podman run --rm \
--mount type=image,source=quay.io/myorg/hello-world:1.0.0,destination=/skills \
my-agent:latestThis works with both Podman and Docker on Linux. On macOS, the
remote client does not support --mount type=image, but you
can create an image-backed volume instead:
podman pull quay.io/myorg/hello-world:1.0.0
podman volume create --driver image \
--opt image=quay.io/myorg/hello-world:1.0.0 hello-world-skill
podman run --rm -v hello-world-skill:/skills:ro my-agent:latestFor Kubernetes / OpenShift, use ImageVolumes (beta in K8s 1.33) to mount skills directly into pods:
volumes:
- name: skill
image:
reference: quay.io/myorg/hello-world:1.0.0
pullPolicy: IfNotPresent
containers:
- name: agent
volumeMounts:
- name: skill
mountPath: /skills
readOnly: trueYou can run skillctl directly on an OpenShift cluster to inspect images in the internal registry. First, create a secret with your registry credentials:
oc create secret docker-registry skillctl-auth \
--docker-server=image-registry.openshift-image-registry.svc:5000 \
--docker-username=unused \
--docker-password="$(oc whoami -t)"Then run skillctl as a pod with the secret mounted:
oc run skillctl --rm -i --restart=Never \
--image=ghcr.io/redhat-et/skillctl:latest \
--overrides='{"spec":{"containers":[{"name":"skillctl","image":"ghcr.io/redhat-et/skillctl:latest","args":["inspect","--tls-verify=false","image-registry.openshift-image-registry.svc:5000/NAMESPACE/SKILL@sha256:DIGEST"],"volumeMounts":[{"name":"auth","mountPath":"/home/skillctl/.docker"}]}],"volumes":[{"name":"auth","secret":{"secretName":"skillctl-auth","items":[{"key":".dockerconfigjson","path":"config.json"}]}}]}}'Use --tls-verify=false for the internal registry (self-signed
certificate). The -i flag ensures --rm cleans up the pod
after it completes. The token from oc whoami -t is typically
valid for 24 hours; recreate the secret when it expires.
make test # Run all tests
make lint # Run golangci-lint
make fmt # Format code| Path | Description |
|---|---|
cmd/skillctl/ |
CLI entry point |
internal/cli/ |
Cobra commands |
pkg/skillcard/ |
SkillCard parse, validate, serialize |
pkg/oci/ |
OCI image pack/push/pull/inspect/promote |
pkg/lifecycle/ |
State machine, tag rules |
schemas/ |
JSON Schema for SkillCard |
examples/ |
Sample skills |
docs/ |
Design specs and research |
Library-first: core logic lives in pkg/ as importable Go
packages. The CLI is a thin consumer. Agent runtimes, CI/CD
pipelines, and other tools can import the library directly.
consumers: skillctl CLI, agent runtimes, CI/CD
|
pkg/ (public Go API)
+-- skillcard/ parse, validate, serialize
+-- oci/ pack, push, pull, inspect, promote
+-- lifecycle/ state machine, tag rules
|
OCI registries (quay.io, ghcr.io, Zot)
draft --> testing --> published --> deprecated --> archived
| State | OCI tag | Example |
|---|---|---|
| draft | <ver>-draft |
1.0.0-draft |
| testing | <ver>-testing |
1.0.0-testing |
| published | <ver> + latest |
1.0.0 |
| deprecated | <ver> |
1.0.0 |
| archived | tag removed | digest only |
Status is stored in OCI manifest annotations
(io.skillimage.status), not inside the image. Image content
is immutable across promotions.
Several projects explore packaging AI agent skills as OCI artifacts. We share the same vision and welcome collaboration.
| Project | Author | Approach |
|---|---|---|
| Agent Skills OCI Artifacts Spec | Thomas Vitale | Specification for skills as ORAS artifacts with Arconia CLI |
| skills-oci | Mauricio Salatino | CLI for skills as OCI artifacts with SLSA provenance and SBOMs |
| skillctl (this project) | Red Hat OCTO | Skills as OCI images for multi-user OpenShift/K8s with lifecycle management, read-only ImageVolume mounting, and standard container tooling |
Apache-2.0