Skip to content

feat(skills): pull skills from OCI registries via init container#259

Merged
amcheste-ai-agent[bot] merged 1 commit into
developfrom
feat/oci-skills
Jun 2, 2026
Merged

feat(skills): pull skills from OCI registries via init container#259
amcheste-ai-agent[bot] merged 1 commit into
developfrom
feat/oci-skills

Conversation

@amcheste-ai-agent
Copy link
Copy Markdown
Contributor

Summary

Adds OCI as a SkillSource alongside the existing ConfigMap-backed
skills. Knowledge-work agents need a way to ship versioned, signed,
shareable expertise (financial analysis templates, research playbooks,
output formats) — OCI artifacts give us that distribution story
without inventing a new one.

ConfigMap-backed skills keep working unchanged. Both source types can
coexist on the same teammate.

How it works

Per-skill init container (`pull-skill-{name}`) runs `oras pull`
into a per-skill emptyDir mounted at `/var/claude-skills/{name}/`.
The existing runner entrypoint already copies that path into
`~/.claude/skills/{name}/` before launching Claude Code, so the
main container needs no change.

For private registries, list a `kubernetes.io/dockerconfigjson`
Secret under `spec.imagePullSecrets`. The operator both (a)
propagates it to the pod for kubelet pulls, and (b) projects its
`.dockerconfigjson` into the init container at
`/auth/.docker/config.json` with `DOCKER_CONFIG=/auth/.docker` so
ORAS picks credentials up natively. First listed secret wins —
multi-registry deployments combine credentials into a single
dockerconfigjson.

API

  • `AgentTeamSpec.imagePullSecrets` — `[]LocalObjectReference`
  • `SkillSource.oci` no longer marked "not yet implemented";
    documented re-pull semantics (no shared cache between pods → pin to
    digests for cheap registry short-circuit)
  • `SkillSpec` CEL `XValidation`: exactly one of `configMap` or `oci`

Operator flag

`--skill-puller-image` overrides the default
`ghcr.io/oras-project/oras:v1.2.0`, for air-gapped clusters that
mirror to an internal registry.

Tests

`internal/controller` rose to 86.2% coverage. Five new
`buildAgentPod` cases cover: OCI emptyDir + init container, private
registry auth wiring (DOCKER_CONFIG + Secret volume + dockerconfigjson
projection), ConfigMap path unchanged, mixed sources on one teammate,
and the puller image override.

Docs

`docs/how-to/skill-authoring.md` walks the full lifecycle: SKILL.md
authoring, `oras push`, referencing from an AgentTeam (public +
private), digest pinning, air-gapped clusters. Wired into mkdocs nav.

Sample

`config/samples/oci-skills-team.yaml` — a research team pulling
one public skill and one private skill via imagePullSecrets.

Test plan

  • CI green
  • `kubectl apply -f config/samples/oci-skills-team.yaml` parses cleanly

🤖 Generated with Claude Code

Adds OCI as a SkillSource alongside the existing ConfigMap-backed
skills. Knowledge-work agents need a way to ship specialized,
versioned expertise (financial analysis, web research, report
templates) that isn't best modeled as YAML manifests glued to a
team CR. OCI artifacts give us a versioning, signing, and
distribution story that already exists in every cluster.

Reconciler
- New per-skill init container ("pull-skill-{name}") runs `oras pull`
  into an emptyDir mounted at /var/claude-skills/{name}/. The
  existing runner entrypoint already copies that path into
  ~/.claude/skills/{name}/, so the main container needs no change.
- ConfigMap skills continue to work unchanged; both source types
  can coexist on the same teammate.
- spec.imagePullSecrets are propagated to the Pod (kubelet pulls)
  and additionally projected as a `.dockerconfigjson` → config.json
  Secret volume on the init containers, with DOCKER_CONFIG=/auth/.docker
  so ORAS picks creds up natively. The first listed pull secret is
  used; multi-registry deployments combine credentials into a single
  dockerconfigjson.
- Default puller image is ghcr.io/oras-project/oras:v1.2.0,
  overridable per-cluster via the --skill-puller-image manager flag
  for air-gapped or mirror deployments.

API
- AgentTeamSpec.imagePullSecrets — list of LocalObjectReference; the
  same Secrets the kubelet sees for image pulls also flow into the
  ORAS init container's auth mount.
- SkillSource: dropped the "not yet implemented" TODO on the OCI
  field and documented re-pull semantics.
- SkillSpec gains a CEL XValidation rule enforcing exactly one of
  configMap or oci.

Docs
- docs/how-to/skill-authoring.md walks through the full lifecycle:
  authoring a SKILL.md directory, packaging with `oras push`,
  referencing from an AgentTeam (public + private registries), and
  the practical notes on digest pinning + air-gapped clusters.
- Wired into mkdocs nav under a new "Author skills" how-to group.

Sample
- config/samples/oci-skills-team.yaml exercises both a public
  registry skill and a private one through imagePullSecrets.

Tests
- internal/controller coverage rose to 86.2%. Five new
  buildAgentPod cases cover: OCI emptyDir + init container, private
  registry auth wiring (DOCKER_CONFIG + Secret volume + dockerconfigjson
  key/path projection), ConfigMap path unchanged, mixed sources on
  one teammate, and SkillPullerImage override.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: amcheste <13696614+amcheste@users.noreply.github.com>
@amcheste-ai-agent amcheste-ai-agent Bot requested a review from amcheste as a code owner June 2, 2026 14:33
@github-actions github-actions Bot added the docs label Jun 2, 2026
@amcheste-ai-agent amcheste-ai-agent Bot merged commit 3b2a430 into develop Jun 2, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant