feat(docker): declare the minimal CogOS workspace as a container image - #574
Draft
chazmaniandinkle wants to merge 1 commit into
Draft
feat(docker): declare the minimal CogOS workspace as a container image#574chazmaniandinkle wants to merge 1 commit into
chazmaniandinkle wants to merge 1 commit into
Conversation
Dockerfile.workspace is the first official declaration of what a CogOS workspace IS, not just how the kernel runs. It fetches a checksum- and Sigstore-verified published release the same way self-update does, then scaffolds the workspace by running the real `cogos init` (not a hand-copied file list), so the image can never drift from what init.go actually produces. A build-time assertion checks the cog.workspace.schema label against .cog/VERSION so the label cannot silently lie. docker-compose.workspace.yml composes this image with the existing kernel Dockerfile as a separate file rather than an addition to docker-compose.node.yml, to avoid touching that file's multi-node bind-mount topology for an unrelated concern. Refs #571.
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 this declares
Dockerfile.workspaceis the first official declaration of a CogOS workspace as an image — as distinct from the existingDockerfile, which containerizes only the kernel binary. Every existing container artifact in this repo (Dockerfile,Dockerfile.e2e,docker-compose.node.yml) mounts a workspace in from the host; nothing declares what a fresh workspace actually is. This does.Two things make it trustworthy rather than a hand-copied file list:
scaffoldbuild stage runs the actualcogos init --workspace /workspace(internal/engine/init.go'sRunInit) against a downloaded release binary — not a manually-copied directory tree. Ifinit.gochanges what init produces, this image changes with it the next time it's rebuilt against a release containing that change.internal/providers/selfupdate/github.goresolvescogos-<goos>-<goarch>,checksums.txt,checksums.txt.sig,checksums.txt.pemfor a release;docs/release-signing.mddocuments the consumer recipe (cosign verify-blobagainst this repo'srelease.ymlCI identity, thensha256sum -c). Thefetchstage does exactly that, and fails the build (REQUIRE_SIGNATURE_VERIFICATION=1, the default) if either check doesn't pass.cogos initcurrently scaffolds (from readinginternal/engine/init.godirectly):.cog/config,.cog/agents/identities,.cog/mem/{semantic,episodic,procedural,reflective,working},.cog/ledger,.cog/run,.cog/blobs.cog/config/kernel.yaml,.cog/config/identity.yaml,.cog/config/providers.yaml,.cog/agents/identities/identity_cogos.md.cog/VERSIONmarker (currently3.0.0)Component seams (build ARGs)
COGOS_VERSION— pinned release tag (defaultv0.16.31, the latest published tag as of this writing; not resolved dynamically, so builds stay reproducible)WORKSPACE_NAME,COGOS_PORT— cosmetic identity / port, stamped as labels/envSKILLS_OVERLAY,CONFIG_OVERLAY— build-context-relative directories COPYed onto.claude/skills/and.cog/config/after scaffolding. Default to empty placeholders checked in atdocker/workspace-overlay/{skills,config}/, so a plaindocker buildproduces zero overlay. NoONBUILD; a distribution just points these ARGs at its own directories inside its build context.REQUIRE_SIGNATURE_VERIFICATION— fail-closed by default;0only for pre-signing releases (< v0.16.20) or air-gapped builds.COGOS_WORKSPACE_SCHEMA(default3.0.0) — thecog.workspace.schemaOCI label. Thescaffoldstage asserts this equals the actual.cog/VERSIONcogos initwrote, so the label can never silently drift from reality — bump the ARG wheninit.go's VERSION marker changes, or the build fails loudly.What is deliberately NOT in this image
No
ANTHROPIC_API_KEYor any other secret, no provider config beyond shipped defaults, no identity beyond the generic default identity card, no skills, noCLAUDE.md/SOUL.md/USER.md. A workspace is a substrate, not a persona — those are supplied by whoever runs the image, same as the kernelDockerfile's external workspace volume mount today.Composition
docker-compose.workspace.ymlis a new, separate compose file (not an addition todocker-compose.node.yml): aworkspace-initservice builds the workspace image, materializes its scaffolded/workspaceinto a named volume, and exits; the existing kernelDockerfilethen mounts that volume onceworkspace-initcompletes successfully.docker-compose.node.yml's primary/secondary topology already assumes an externally bind-mounted workspace across several services (vaultwarden, gateway, bridge, tailscale siblings) — threading a build-time-scaffolded workspace through that would mean touching every node's volume wiring for a concern this file demonstrates cleanly in two services, with zero risk to the existing topology.docker-compose.node.ymlis untouched.Docs
Added a "Workspace base image" subsection to README.md's existing
### Dockersection (where the repo already documentsdocker-compose.node.yml): states plainly thatDockerfile.workspaceis the declared minimal starting condition of a workspace,cog doctorvalidates it, this image materializes it, and distributions layer overlays on top.Verification actually performed
No Docker daemon was available on this host (
docker info→Cannot connect to the Docker daemon at unix:///Users/slowbro/.docker/run/docker.sock), so the image was not built or run.docker buildx build --checkwas attempted and failed for the same reason (needs a builder backed by a running daemon). What was done instead:Dockerfile's working multi-stage patterns (builder/runtime split,apk add --no-cache, non-rootcogosuser viaaddgroup -S/adduser -S,HEALTHCHECKshape).internal/engine/init.go,internal/engine/cli.go(command dispatch,runHealthCheckCmd,runInitCmd),internal/providers/selfupdate/github.goandresolve.go,docs/release-signing.md, and.github/workflows/release.ymldirectly, rather than assuming their behavior.alpine:3.21's current multi-arch manifest-list digest live against the Docker Hub registry API (sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d) to pin the base image by digest while keeping multi-platform resolution — this is a real, current digest as of 2026-08-21, not fabricated, but it will need periodic bumping like any digest-pinned base.v0.16.31is the current latest publishedcogosrelease (gh release list) and that it postdatesFirstSignedRelease = v0.16.20, so signature verification is exercised by default with the chosenCOGOS_VERSIONdefault.cosignv3.1.3is current and thatsigstore/cosignpublishes acosign_checksums.txtits own binary can be checksummed against.HEALTHCHECK/CMD(ARGs don't survive into the running container;EXPOSE/LABEL/COPYget Dockerfile-level substitution,CMD/HEALTHCHECKdon't — they needENVplus shell-form to pick up$COGOS_PORTat container start) since this class of bug is easy to introduce and silent until runtime.I did not get to execute a real build, so there is real risk of a typo-class Dockerfile bug (a missing stage reference, a shell quoting slip) that only surfaces in an actual
docker build. Please build it before merging, or ping me to arrange a host with a running daemon.Design decisions the operator should ratify
cogos, notcog. The existing kernelDockerfilebuilds from source and names its entrypoint binarycog(COPY --from=builder /cog /usr/local/bin/cog). This image installs the published release asset, which GitHub Actions namescogos-<goos>-<goarch>and which self-update/install docs refer to ascogos(e.g.~/.cog/bin/cogos). I kept the published name (/usr/local/bin/cogos,ENTRYPOINT-equivalentCMD cogos ...) rather than renaming it to match the kernel image'scogconvention. Flag if the two images should agree on one binary name.docker-compose.node.yml. Explained above under Composition — open to revisiting if a tighter integration is wanted later.COPYbehind ARGs, notONBUILDor a templated Dockerfile. This satisfies "ONBUILD-free template hooks" literally, but it does mean a distribution's overlay directory must live inside this build's context (or the distribution builds from adocker build -f path/to/Dockerfile.workspace ..pointed at a context that contains both). An alternative (not implemented) is a distribution doingFROM ghcr.io/myrgic/cogos-workspace:TAGin its own Dockerfile andCOPYing on top of the published image instead of rebuilding from source with different ARGs — probably the more common real-world pattern once this image is actually published toghcr.io. Worth deciding which pattern is canonical.cog.workspace.schemais pinned to the literal string.cog/VERSIONcurrently holds (3.0.0), asserted at build time. Ifinit.go's VERSION marker is meant to mean something more specific (a scaffold-shape version distinct from a general workspace format version), the assertion still holds but the semantic label content may need a different source of truth.workspace-initin the compose file runs as root andchmod -R a+rwXs the shared volume rather than trying to coordinate matching UIDs between two independently-built Alpine images. Documented inline as a compose-demo simplification, not a production pattern.HEALTHCHECK/CMDuse shell-form and anENVmirror ofCOGOS_PORT(notENTRYPOINT+ exec-formCMDlike the existing kernel Dockerfile) because exec-form JSON arrays never expand$COGOS_PORT— Docker only substitutes ARGs intoLABEL/EXPOSE/COPY/etc., not intoCMD/ENTRYPOINT/HEALTHCHECKcontent. Flagging since it's a real behavioral divergence from the existing Dockerfile's style, made necessary by wanting the port to be genuinely build-configurable.Refs #571 — this is the materialize-side counterpart to
cog doctor(#570)'s declared/validated surface: doctor checks an existing install/workspace against what healthy looks like; this Dockerfile is the thing that produces a workspace doctor should find healthy.