Session C groundwork: Werkator's git-metadata mask needs a tmpfs BETWEEN binds (ro-bind .git, tmpfs .git/werkator, bind workspace), so -v and --tmpfs now collect into one ordered mount list and RunSpec carries Mounts instead of Binds. --tmpfs DEST is the docker flag of the same name. `werkdock images` lists loaded image names one per line, so a consumer can check existence through the CLI. -v accepts the explicit :rw docker default instead of refusing it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Werkdock
A docker-like sandbox CLI over bwrap — filesystem isolation only.
A dock is the enclosed basin in which ships are built: the dock gate controls what passes, the water outside is shared with the whole harbor.
Accordingly, network, uid, /proc, /dev, and /tmp come from the host by contract; that is what makes Werkdock work without root on a Hostsharing Managed Webspace.
Semantics — docker-compatible as far as the filesystem-only contract allows (see RFC 0002):
- An image is a rootfs archive; an instance is an unpacked, writable directory tree and corresponds to a docker container.
werkdock run [flags] IMAGE [CMD...]creates an instance and executes in the sandbox with uid 0 mapped to the calling user; verbs and flags follow docker, unsupported docker flags fail loudly.werkdock doctorchecks the host: user-namespace capability, disk and quota headroom.- A daemon speaking the Docker Engine API subset (for Testcontainers) is designed for but deferred.
Disk Footprint
Werkdock's storage model is coarser than Docker's on the image side and cheaper on the instance side:
- An image is a flat, complete directory tree — there are no layers, and nothing is shared between images. A JDK+Go+Node build image is roughly 2 GiB unpacked, plus its compressed archive (~0.5 GiB) as long as that is kept around.
- An instance costs (almost) nothing: the rootfs is bound read-only into every sandbox, writable are only tmpfs (
/tmp,/root) and the caller's binds. Ten parallel runs in one image add zero filesystem copies; what grows per project are its own caches in bound volumes. - Consequence: prefer ONE fat image shared by all projects over per-project images.
- Watch out for orphans: consumers that key an unpacked environment by the archive's source path (Werkator's bwrap runtime does) leave the old tree behind on every path change; pruning is manual until
rmi/pruneverbs exist. - Future options that would remove the flat-tree cost, in their own RFCs when they come due: composable toolchain mounts — a slim base plus per-toolchain prefix binds, no overlayfs needed (RFC 0003, candidate) — overlayfs layers (the kernel allows it unprivileged in a user namespace since 5.11; the webspaces' bwrap 0.8.0 cannot yet), or hardlink deduplication between image versions in the store (the ostree principle, no root needed).
Build and Test
go test ./... # all tests; sandbox integration tests skip without bwrap/userns
go vet ./... && gofmt -l . # quality gates (gofmt must print nothing)
CGO_ENABLED=0 go build . # one static linux binary, ~3 MB
First steps on a host:
werkdock doctor # can this host run sandboxes?
werkdock load -i rootfs.tar.zst # import a rootfs archive as an image
werkdock run --rm -v /repo:/repo -w /repo IMAGE sh -c './gradlew build'
Status: bootstrap. The implementation language is Go, decided in RFC 0001. Werkdock grows in this subdirectory of the Werkator repository and moves to its own repository once it stands on its own. It must stay self-contained: no imports from Werkator code, no Gradle coupling to the Werkator build. The roadmap is session B of docs/plan/21-werkdock-extraction-and-webspace-install.md.