feat(template): versioned OCI template registry — push/add/update/diff/verify (spec 19) - #108
Merged
Merged
Conversation
…mote Docker host (spec 21) Generalize the shared stack to run on a remote Docker host (an SSH/TCP `docker context` or a DOCKER_HOST endpoint) instead of only the local daemon. Ref-counting, provisioning, and DNS/alias are backend-agnostic; this swaps WHERE containers run. Default is local, byte-for-byte unchanged. - internal/docker: a Backend seam (ComposeEnv/Reachability/NewClient/ RemoteReachable). Local = zero value (unchanged). Remote binds moby to the resolved endpoint and keys the ledger by the context name/host so remote rows never bleed into local counts, like the WSL2 Desktop-vs-dockerd split. - internal/docker: Compose gains ContextEnv, appended after the secret Env, pinning every compose verb to the endpoint (DOCKER_HOST/DOCKER_CONTEXT). - internal/config: a workspace `backend:` selector (+ store global default); validated (dockerhost scheme; context XOR host). - orchestrate/up + cli/up: thread the backend into the client, ledger key, and every compose driver. Remote host-side Postgres provisioning is guarded with a clear error (scoped follow-up — needs an SSH tunnel). - doctor: a backend.remote reachability probe with remediation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…f/verify (spec 19) Add internal/registry: a pure-Go (oras-go/v2, CGO-free) OCI template-registry seam behind a TargetResolver interface (real remote.Repository w/ docker creds; tests round-trip through an in-memory oras store, no network). Deterministic bundle tar packaging (content-addressed digest), mandatory digest verification on pull, and a cosign Verifier/Signer seam (shells cosign, matching internal/selfupdate's CGO-free precedent; mock-able for tests). Graduate the template.go stubs to real commands: push, add (tag→digest pin + verify + digest-keyed cache + lockfile entry, lock-guarded), update (re-resolve w/ --dry-run), diff (render-diff pinned vs remote), verify (re-pull + signature), and ls. Store config gains a digest-pinned `templates:` lockfile; remote templates are chained into the generate/lint/test source (embedded < store < remote), digest-pinned and offline-first so generation stays byte-deterministic. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gustavobertoi
force-pushed
the
feat/template-registry
branch
from
July 1, 2026 16:51
fcd14aa to
ddaa465
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Implements spec 19 — a versioned OCI template registry — turning the embedded template pile into digest-pinned, shareable team infrastructure.
New:
internal/registryPure-Go (
oras.land/oras-go/v2, CGO-free) OCI seam behind a smallTargetResolverinterface so the CLI never touches oras directly and tests round-trip through an in-memory oras store (no network):reference.go— parseoci://host/repo:tag/@sha256:…(+ParseRepositoryfor the bare lockfilesource:), reject floating:latest.bundle.go— deterministic tar packaging of a template dir (sorted paths, zeroed mtime/uid/gid, fixed modes) so the artifact digest is a pure function of content; atomic, traversal-safe, size-capped unpack.registry.go/pull.go/target.go—Push/ResolveDigest/Pull, customartifactType+ layer media type, docker/ORAS credential store +GITHUB_TOKENfor ghcr.io (no devstack token store), auth-error remediation. Mandatory digest verification on pull (a re-pushed/tampered manifest is refused; nothing is unpacked).verify.go— cosignVerifier/Signerseam (keyless + keyedVerifyPolicy), mock-able; a zero policy = digest-pinned-but-unsigned notice.Graduated
templateverbs (were stubs)push,add(tag→digest pin + optional signature verify + digest-keyed cache + lockfile entry, all underlock.WithLock),update(re-resolve,--dry-run,--to),diff(render-diff pinned vs remote tag),verify(re-pull + re-check digest/signature),ls.Wiring
internal/storegains a digest-pinnedtemplates:lockfile block + a digest-keyed cache underXDG_CACHE_HOME/devstack/templates/<algo>/<hex>(never keyed by tag → no cache poisoning).Why
Multiple repos can now render byte-identical compose/Dockerfiles from a committed, digest-pinned lockfile; a moving tag never reaches the hot path.
How tested
internal/registry: push+pull round-trip through an oras memory store;PackBundledeterminism (metadata-independent); digest-mismatch refusal; reference-parse table; verify good vs tampered (fake verifier); unavailable-cosign abort.internal/cli: full add/update/diff/verify/ls flow through a memory-backed client seam — digest pinned into the store lockfile, bundle cached under the digest key and resolvable by name; drift detected +--dry-runwrites nothing + real update moves the pin; newer-schemaVersionbundle rejected with an upgrade message; floating-tag refusal; signature policy accept/reject.CGO_ENABLED=0 go build ./...,CGO_ENABLED=1 go test ./internal/...,gofmt -lclean,go vet ./...,make determinism.Scoped out (flagged in code)
templates:/verify.cosignlockfile + generate wiring — this slice keeps the registry lockfile in the store config (~/.devstack/config.yaml); theRemoteTemplateshape is identical to the spec's workspace block so migration is a move, not a redesign.cosign, consistent withinternal/selfupdate's documented CGO-free decision; the seam makes a sigstore-go swap drop-in.🤖 Generated with Claude Code