Problem
Teams whose repos use devenv need a base image with Nix + devenv + direnv baked in — create-time installs are impractical (the Nix closure is GBs, and kit egress policies rightly block package mirrors). Our team (login.gov Team Data) has run such a template in production since June (data-warehouse-ag sbx-templates/team-data-devenv/, ADR-0003), but it lives in a team repo, and distribution is "every teammate builds or loads a 4.7 GB tar by hand." Nothing in the image is actually team-specific — the team layer lives entirely in our acq kit. That makes it a natural patterns integration: one published, generic devenv template any team can point ACQ_IMAGE at.
Proposal
Add the Dockerfile (we'd contribute ours, generalized) under integrations/isolation/, plus a GitHub Actions workflow that builds and pushes it to GHCR as a repo-linked package (workflow GITHUB_TOKEN with packages: write; public visibility → anonymous pulls).
Consumption is the same one-liner on both backends, via the neutral image knob (ADR-0022): ACQ_IMAGE=ghcr.io/gsa-tts/<name>:<tag> — sbx pulls it as a template ref, msb as a plain OCI ref. The load-a-tar-per-machine flow (and its stale-loaded-template failure class) disappears.
Design points
Base image: parametrized, defaulting to the agent-less family base. ARG BASE_IMAGE=docker/sandbox-templates:shell-docker — the same base msb defaults to (agent user, node, agent-writable npm prefix, git/curl/ca-certificates guaranteed), valid on sbx as the shell agent's base. The devenv payload layers (Nix single-user, devenv, direnv, common CLI tools) are base-agnostic, so CI builds a small matrix: devenv-shell (generic; msb installs its agent at provision) and devenv-opencode (sbx-native opencode attach, what our team runs today). More agent bases are one matrix entry each. As kit-delivered agents mature (the prime-agent kit, #369, is exactly this pattern), the matrix can collapse toward the shell variant.
No baked proxy CA. Our current Dockerfile bakes the Zscaler root because a local build behind the proxy needs it before any kit exists. CI builds don't, and runtime trust inside sandboxes is already the zscaler-ca-certificate kit's job (acq orders it first, and the image pins Nix at the system CA bundle, which that kit updates). Baking an interception root into a public generic image would also force non-GSA consumers to trust it — wrong default. We'd keep an ARG EXTRA_CA_CERT="" (install-if-provided) so behind-proxy local rebuilds stay possible.
Multi-arch. Our current image is aarch64-only (built on Apple Silicon). The workflow should build a multi-arch manifest (amd64 + arm64) using the native arm runners available to public repos — QEMU emulation is not viable for a Nix-closure build.
Build weight and trigger. It's a multi-GB, tens-of-minutes build — trigger on changes to the Dockerfile/template dir (plus manual dispatch), not on every PR; use registry layer caching. Tag scheme: an explicit version tag bumped with toolchain changes (our repo's convention: the tag encodes the pinned nixpkgs rev/toolchain, never reused), plus a moving latest if you want one.
What stays out. Team-specific config (instructions, egress, OpenCode settings, glab pointing at a specific GitLab) remains in team kits — the split our stack already has. Teams with sized-store needs pair the image with a kit-declared volumes: (ADR-0023) exactly as our kit does for /nix.
Prior art in this repo
The paseo and openchamber kits established the pattern of patterns-hosted, opt-in integrations with their own delivery mechanics; the USAi catalog work established CI-built artifacts with byte-exact guarantees. This proposal combines the two: a CI-built, registry-delivered base artifact that kits layer on.
We'd contribute the generalized Dockerfile and the workflow, and validate the result against our production kit on both backends (our verify suite covers the sbx path end-to-end; we've run the msb path this week).
Problem
Teams whose repos use devenv need a base image with Nix + devenv + direnv baked in — create-time installs are impractical (the Nix closure is GBs, and kit egress policies rightly block package mirrors). Our team (login.gov Team Data) has run such a template in production since June (data-warehouse-ag
sbx-templates/team-data-devenv/, ADR-0003), but it lives in a team repo, and distribution is "every teammate builds or loads a 4.7 GB tar by hand." Nothing in the image is actually team-specific — the team layer lives entirely in our acq kit. That makes it a natural patterns integration: one published, generic devenv template any team can pointACQ_IMAGEat.Proposal
Add the Dockerfile (we'd contribute ours, generalized) under
integrations/isolation/, plus a GitHub Actions workflow that builds and pushes it to GHCR as a repo-linked package (workflowGITHUB_TOKENwithpackages: write; public visibility → anonymous pulls).Consumption is the same one-liner on both backends, via the neutral image knob (ADR-0022):
ACQ_IMAGE=ghcr.io/gsa-tts/<name>:<tag>— sbx pulls it as a template ref, msb as a plain OCI ref. The load-a-tar-per-machine flow (and its stale-loaded-template failure class) disappears.Design points
Base image: parametrized, defaulting to the agent-less family base.
ARG BASE_IMAGE=docker/sandbox-templates:shell-docker— the same base msb defaults to (agent user, node, agent-writable npm prefix, git/curl/ca-certificates guaranteed), valid on sbx as the shell agent's base. The devenv payload layers (Nix single-user, devenv, direnv, common CLI tools) are base-agnostic, so CI builds a small matrix:devenv-shell(generic; msb installs its agent at provision) anddevenv-opencode(sbx-native opencode attach, what our team runs today). More agent bases are one matrix entry each. As kit-delivered agents mature (the prime-agent kit, #369, is exactly this pattern), the matrix can collapse toward the shell variant.No baked proxy CA. Our current Dockerfile bakes the Zscaler root because a local build behind the proxy needs it before any kit exists. CI builds don't, and runtime trust inside sandboxes is already the
zscaler-ca-certificatekit's job (acq orders it first, and the image pins Nix at the system CA bundle, which that kit updates). Baking an interception root into a public generic image would also force non-GSA consumers to trust it — wrong default. We'd keep anARG EXTRA_CA_CERT=""(install-if-provided) so behind-proxy local rebuilds stay possible.Multi-arch. Our current image is aarch64-only (built on Apple Silicon). The workflow should build a multi-arch manifest (amd64 + arm64) using the native arm runners available to public repos — QEMU emulation is not viable for a Nix-closure build.
Build weight and trigger. It's a multi-GB, tens-of-minutes build — trigger on changes to the Dockerfile/template dir (plus manual dispatch), not on every PR; use registry layer caching. Tag scheme: an explicit version tag bumped with toolchain changes (our repo's convention: the tag encodes the pinned nixpkgs rev/toolchain, never reused), plus a moving
latestif you want one.What stays out. Team-specific config (instructions, egress, OpenCode settings, glab pointing at a specific GitLab) remains in team kits — the split our stack already has. Teams with sized-store needs pair the image with a kit-declared
volumes:(ADR-0023) exactly as our kit does for/nix.Prior art in this repo
The paseo and openchamber kits established the pattern of patterns-hosted, opt-in integrations with their own delivery mechanics; the USAi catalog work established CI-built artifacts with byte-exact guarantees. This proposal combines the two: a CI-built, registry-delivered base artifact that kits layer on.
We'd contribute the generalized Dockerfile and the workflow, and validate the result against our production kit on both backends (our verify suite covers the sbx path end-to-end; we've run the msb path this week).