Quick start
In about ten minutes you will write a skill, publish it to a registry, pull it back onto a machine that has never seen it, and install it with your own values filled in. Every command below was run start to finish before it was written down.
No prior knowledge of OCI or registries is assumed.
What you need
A registry is a server that stores versioned, immutable
blobs of data addressed by name and tag — reviewer:1.0.0 —
and by a SHA-256 digest of their bytes. Container images
live in one. So does anything else that follows the same wire protocol,
which is called OCI. A skill packaged by Epos is an
ordinary OCI artifact, so any registry you can already push a container
image to will hold it, and any OCI client can read it back.
You need three things:
- Epos.
go install github.com/gaarutyunov/epos/cmd/epos@latest -
oras, the reference OCI command-line client. It does the publishing step, for the reason in step 4. Installation instructions are at oras.land. - A registry you can write to. If your code is on
GitHub you already have one:
ghcr.io. If you would rather not touch a public registry at all, run one locally — see the note in step 4.
Everything below happens in one empty directory. Make one and stay in it:
1. A throwaway store
Epos keeps every skill it packs, builds or pulls in a local
store: a directory of content-addressed blobs, the same idea as
the Go module cache or ~/.docker. By default it lives under
~/.epos. EPOS_HOME moves it, which is what
makes this tutorial safe to try and easy to undo — put the store inside
the directory you just made, and deleting that directory deletes
everything the tutorial created.
/home/you/epos-quickstart/.epos-store/store
~/.epos.
3. Pack it
epos pack turns the directory into an artifact and writes
it into the local store. It reads the name and version out of the
frontmatter, so it needs no arguments beyond the directory.
reviewer:1.0.0 sha256:a32fa1dfea1118829a06ab4975e38ca502cc3c3f1ccfdab909f846276f486558
The tag is on the left and the digest is on the right. The digest is a
hash of the artifact's bytes, and packing is deterministic: pack the
same directory on another machine, on another operating system, and you
get that same a32fa1df… back. That is what makes the digest
worth pinning.
reviewer:1.0.0
4. Publish it
Publishing is epos push
— a direct copy from the local store to your registry, with
epos registry login for the
credential. What was withdrawn is publishing through epos-registry, the component that fronts your registry to
count downloads: it would have had to redirect the upload session to the
registry behind it, and oras-go refuses an upload redirect
that crosses hosts. That refusal is the fix for a real credential-leak
advisory, so it is not something to work around — and it does not apply
to a client pointed straight at your registry, which is what
epos push is. See SPEC §4.5 for the full reasoning.
Any other OCI client works too, because the artifact Epos produced is not
an Epos format. It is a plain OCI artifact, so plain OCI tools handle it —
which is what the rest of this section shows with oras.
Point at your registry and log in:
Login Succeeded
Then copy the artifact out of the local store and into the registry.
Epos's store is an OCI image layout — a directory in the format the OCI
specification defines for holding artifacts on disk — which is exactly
what oras cp can read from:
Copying 11b90ecf569e application/vnd.agentskills.skill.config.v1+json Copying 4ddd04a7449c application/vnd.agentskills.skill.content.v1.tar+gzip Copied 4ddd04a7449c application/vnd.agentskills.skill.content.v1.tar+gzip Copied 11b90ecf569e application/vnd.agentskills.skill.config.v1+json Copying a32fa1dfea11 reviewer Copied a32fa1dfea11 reviewer Copied [oci-layout] reviewer:1.0.0 => [registry] ghcr.io/your-github-username/agent-skills/reviewer:1.0.0 Digest: sha256:a32fa1dfea1118829a06ab4975e38ca502cc3c3f1ccfdab909f846276f486558
Same digest as epos pack printed. Publishing copies bytes;
it does not rewrite them.
The repository name is <namespace>/agent-skills/<skill>
by convention: one skill per repository, so the repository name alone
says which skill it holds.
oras cp --from-oci-layout <dir>:<tag>
form splits its argument at the last colon, and an Epos store tag
contains a colon — reviewer:1.0.0 — so it would
look for a directory called …/store:reviewer.
--from-oci-layout-path keeps the two apart. Addressing the
artifact by digest works with the short form too:
oras cp --from-oci-layout "$(epos store path)@sha256:a32fa1df…"
"$REGISTRY/agent-skills/reviewer:1.0.0".
zot is a small OCI registry that needs no configuration:
$ docker run -d -p 5000:5000 ghcr.io/project-zot/zot-linux-amd64:v2.1.18 $ export REGISTRY=localhost:5000/you
It speaks HTTP rather than HTTPS, so add --to-plain-http
to the oras cp above and --plain-http to the
epos pull in the next step. Skip the
oras login: it has no authentication. Everything else in
this tutorial is unchanged — it is the same protocol either way.
5. Pull it back
Point EPOS_HOME somewhere new. That store is empty, so what
happens next can only come from the registry — it is the same situation
a colleague, or your CI, starts in.
(nothing) reviewer:1.0.0 sha256:a32fa1dfea1118829a06ab4975e38ca502cc3c3f1ccfdab909f846276f486558
reviewer:1.0.0
The digest is the one that was packed and the one that was published. That is the whole point of a content-addressed artifact: you can tell, without trusting anybody, that what arrived is what left.
pull takes the skill's name from the repository — the last
path segment — and tags it in the store as
<name>:<version>. That short tag is what you
install by.
6. Install it with values
Installing is where the {{ }}
placeholders finally get filled in. Nothing rendered them earlier: the
artifact in the registry still holds them verbatim, and it can be
installed a hundred times with a hundred different answers.
Write the answers down:
values.yaml
title: The reviewer language: Go model: opus global: org: Acme
installed into .claude/skills/reviewer reviewer:1.0.0 sha256:a32fa1dfea1118829a06ab4975e38ca502cc3c3f1ccfdab909f846276f486558
--- name: reviewer version: 1.0.0 description: Reviews a change and reports what to fix. model: 'opus' --- # The reviewer Review Go changes for Acme. See `references/checklist.md` before you start.
Every placeholder is gone, including the one in the frontmatter, and the
quotes that made it legal YAML are still there.
{{ .Values.global.org }} came from the
global block: values are scoped, and global is
the block every part of a skill can see — which matters once a skill is
composed from several others.
--set for one-off answers
--set k=v overrides the file, key by key, and dots name
nested keys. It is repeatable, and it always wins over every
-f file:
--- name: reviewer version: 1.0.0 description: Reviews a change and reports what to fix. model: 'haiku' --- # The reviewer Review Go changes for Contoso. See `references/checklist.md` before you start.
title and language still came from the file;
model and global.org came from the flags.
What install wrote down
Two files appeared next to values.yaml.
skills.json records what you asked for.
skills.lock.json records the digest that answered:
{
"lockfileVersion": 1,
"skills": [
{
"name": "reviewer",
"version": "1.0.0",
"ref": "reviewer:1.0.0",
"digest": "sha256:a32fa1dfea1118829a06ab4975e38ca502cc3c3f1ccfdab909f846276f486558",
"basePaths": [
".claude/skills"
]
}
]
} Commit both. The store is a cache and the lock is the truth, so the next machine resolves the same bytes you did, and two checkouts on one machine can sit on two different versions at the same time.
$ epos install reviewer:1.0.0 --set title=Reviewer Error: render SKILL.md: it uses a value that was not supplied
epos install reads the local store and never touches the
network. Run epos pull — or epos build — first.
7. ls and uninstall
epos ls prints what this directory is pinned to. It reads
the lock file, not the filesystem, because the lock is what the
directory actually installed:
reviewer:1.0.0 sha256:a32fa1dfea1118829a06ab4975e38ca502cc3c3f1ccfdab909f846276f486558
epos uninstall takes the rendered skill back out and drops
it from both manifests:
removed .claude/skills/reviewer
(nothing) The store keeps its copy, so reinstalling costs nothing and needs no network:
reviewer:1.0.0
epos ls is the directory; epos store ls is the
cache. epos store prune collects what no tag reaches, when
you want the space back.
8. Clean up
Both stores were inside this directory, which is what
EPOS_HOME bought you. Deleting them leaves nothing behind:
Then unset EPOS_HOME, or close the shell, and Epos is back
on ~/.epos.
Where to go next
You packed a skill you wrote. The other way to make one is to
derive it from a skill somebody else published, with a
Skillfile that reads like a Dockerfile:
Skillfile
FROM ./reviewer APPEND references/checklist.md <<EOF - Public API changes are noted in the changelog. EOF
reviewer:1.1.0 sha256:347657e7fee142b72881c4977275073d59332e5010074ec023e68fec4e9dc895
Install that and the checklist has a fourth line the base never had. The
base can just as well be a skill somebody else published —
FROM ghcr.io/their-org/agent-skills/reviewer:1.0.0 — in
which case the build pins the digest it resolved and records it on the
artifact.
A built skill lands in the store like a packed one, so steps 4 to 7 above work on it unchanged. Nothing executes during a build, and the result is a pure function of the base, the Skillfile and the context — the same three inputs always give the same digest.
Every instruction, with syntax and a worked example, is in the Skillfile reference. Every command you ran above, with its flags and their defaults, is in the CLI reference.