Add Zot OCI registry as ArtifactStore backend (P1 security work) #158
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
This is the deferred OCI-registry follow-up to the init-container JAR fetch work tracked in
docs/superpowers/plans/2026-04-27-init-container-jar-fetch.md(and the multi-tenant hardening epic #152). The earlier work introduces anArtifactStoreinterface incameleer-server-corewith one filesystem implementation. This issue covers adding a second implementation,OciArtifactStore, backed by Zot, and standing up Zot in the deployment stack.Why we deferred (and what changes when we do it)
OCI buys nothing for today's needs. We store JAR bytes and serve them over an authenticated HTTP URL — a registry adds zero value for that and adds real ops cost (a service, storage backend, TLS, auth, backups, monitoring). The payoff arrives only when we start needing what OCI bundles:
These are all explicitly P1 items in #152 (Falco rules, Trivy/Cosign pipeline, JAR SBOM scanner, etc.). When that work starts, OCI is the right shape for everything: customers still upload via REST, the upload path persists into the OCI registry instead of the filesystem, the loader init container still does an
HTTP GETagainst either Cameleer or a pre-signed registry URL — same wire shape, different backend.Why Zot specifically (and not the alternatives)
Considered options:
Zot is the only one that gives us scanning + signing + OCI-native without dragging in a Postgres/Redis side-stack we'd otherwise have to operate.
Migration path
The
ArtifactStoreinterface (shipped in the init-container plan) is the migration insurance. With it in place, this work is bounded to weeks, not a quarter:StatefulSet). TLS via Traefik, auth via htpasswd or Zot's built-in OIDC integration.OciArtifactStoreincameleer-server-app/storage/. Use zotregistry/oras-java (or fall back to Apache HttpClient against the OCI distribution spec endpoints — well documented, ~200 lines).ArtifactStorebean toOciArtifactStore. Init container URLs now resolve against OCI (still HTTP, still token-auth — invisible to the loader).app_versions, copies filesystem → OCI for old entries that never went through dual-write. The OCI ref convention is already locked:ArtifactCoordinates.ociRef()returns{tenantId}/{appId}:v{version}.The
ArtifactCoordinatesvalue type was deliberately designed withociRef()from day one so this migration doesn't need a coordinates schema change — seecameleer-server-core/src/main/java/com/cameleer/server/core/storage/ArtifactCoordinates.java.What this issue does NOT cover
oras pushfrom CI), that's a separate feature and probably not worth building — the REST API is universal across every CI system.AppVersion. Independent of OCI backend choice.Acceptance criteria
OciArtifactStore implements ArtifactStore, allFilesystemArtifactStoreTestcases pass against the OCI impl as wellapp_versions.claude/rules/*updated to reflect OCI as the artifact backendTrigger conditions
Pick this up when any of:
Until then, the filesystem store is the right answer.
References
docs/superpowers/plans/2026-04-27-init-container-jar-fetch.md