Status (updated 2026-06-24) — ✅ ALL 6 SUB-ISSUES MERGED
The managed devcontainer toolchain is fully shipped on main: verified Node fetch (clearsign-verifying real Node releases), per-arch resolution, pinned tools-lock, flag-gated wiring, Linux-gated real-build matrix CI, and the default flipped to managed. Docker is the only host prereq on Linux; the host-npx escape hatch remains for macOS/Windows.
Path to done: #1528 first stalled on a Windows-only CRLF canonical-test failure (fixed via the missing .gitattributes eol=lf). #1530's P0 (real-build matrix feasibility) was resolved by operator decision (b): Linux is the single required/blocking gate; macOS/Windows run non-blocking (continue-on-error) since hosted runners lack Docker. Implementing #1530 then exposed a real supply-chain bug in the merged #1526 verifier — VerifyAndParse used detached-signature verification, but Node publishes SHASUMS256.txt.asc as a clearsigned document, so no real Node release verified. Fixed to clearsign.Decode + verify the embedded checksums, with a regression test routing Node's genuine release fixture through the production verifier (the gap the synthetic detached fixtures missed). #1531 then merged.
Residual follow-ups (all closed)
All sub-issues and residuals resolved — umbrella complete and closed.
Why
To stop redistributing Claude Code (#1451) the Claude agent image must be built on the user's machine. Today Feature-composed images are built by shelling npx --yes @devcontainers/cli@0.87.0 against the host Node: the devcontainerCLI package-level var at internal/sandbox/container/runtime.go:96, the CLI pin DevcontainerCLIVersion at internal/sandbox/container/runtime.go:85, the build argv assembled in devcontainerCLIBuildArgs at internal/sandbox/container/runtime.go:1069, and invoked around internal/sandbox/container/runtime.go:373. A local build therefore adds a host Node/npx prerequisite on top of Docker, and npx on native Windows is the rough spot (shim/PATHEXT/spaces).
The operator's decision is option A: drive devcontainer build with a Node + @devcontainers/cli toolchain Aileron downloads, verifies, caches, and selects per-platform itself, keeping Docker as the only host prereq and preserving the devcontainer Feature composition model at full fidelity (first- and third-party Features, the devcontainer.metadata / customizations.aileron capability labels).
Option B (dockerized devcontainer CLI / docker-outside-of-docker) is rejected for now. Its only advantage — avoiding a from-scratch verified-fetch pipeline on a brand-critical surface — is moot, because Aileron already has a hardened download+verify+cache pipeline (internal/cstore) to reuse. B's docker-socket plumbing is fully greenfield with its own cross-platform matrix (Docker Desktop vs rootless vs Windows) and a socket-mount threat surface that is off-brand. B would trade one cross-platform matrix (Node acquisition, now mostly reuse) for another (socket plumbing, fully greenfield).
This is the keystone that unblocks #1451 and the multi-arch / bake-mcp follow-ons.
Reuse map (~60% is existing machinery)
- Fetch + verify + cache →
internal/cstore: the Fetcher interface and HTTPFetcher (internal/cstore/install.go), the Verifier interface (internal/cstore/verify.go:29), the content-addressed sha256 store under ~/.aileron/store, and atomic-rename concurrency. Fetcher/Verifier are interfaces built for substitution.
- Cross-platform binary location →
internal/launch/launcher.go: resolveSibling, siblingCandidates (.exe suffix), resolveReparsePoints (Windows junctions/symlinks), resolveShimTarget (Scoop shims), and the per-arch selection in resolveSandboxMCPBinary. This covers the Windows tail.
- Version pin + lock → existing pattern: the
agentdigests tool (internal/tools/agentdigests/main.go) + internal/sandbox/composition/agent-images.lock.json, plus DevcontainerCLIVersion.
- Integration seam: the
devcontainerCLI package-level var (internal/sandbox/container/runtime.go:96) exists precisely so resolution can be overridden.
Greenfield (~40%)
- A new
Verifier for Node's GPG-signed SHASUMS256.txt (Node does not use Aileron's Ed25519 keyring), substituted into the cstore pipeline shape; fetch Node distributions by version + GOOS/GOARCH from nodejs.org; unpack.
- M-way arch generalization (the existing selection is effectively two-state host-vs-Linux).
- First-build trigger: detect a missing toolchain and provision on demand.
- Optional
warm/prefetch + offline path (cstore only short-circuits an already-stored hash today).
Dependencies and sequence
The six sub-issues are tracked as native GitHub sub-issues. The approved sequence is:
- Verified Node distribution fetcher (cstore reuse)
- Per-arch managed-binary resolution generalized to M architectures
- Node version+checksum tools lock alongside the CLI pin
- Wire the managed toolchain into
devcontainer build behind the seam, flag-gated (depends on 1, 2, 3)
- Real-
devcontainer build matrix CI and flip the managed default (depends on 4)
warm/prefetch + offline build path (depends on 4; build last)
The managed path lands alongside host-npx behind a config/flag; the default flips to managed only once the matrix CI in sub-issue 5 is green.
Acceptance
- A
devcontainer build (Tier 1, Features applied) succeeds on macOS (both arches), Windows, and Linux using only Aileron's managed Node + @devcontainers/cli — Docker is the only host prereq.
- The managed toolchain is fetched, signature-verified against Node's signed
SHASUMS256.txt, sha256-matched against a committed lock, and cached content-addressed; a tampered checksum or signature is rejected.
- An escape hatch (env var / flag, mirroring the
--runtime override) points at a user-provided Node / devcontainer CLI so no environment hard-blocks.
- On any unsupported platform the build fails fast with a clear message and the manual-fallback hint — no silent half-working, no
t.Skip-when-unsupported.
- The devcontainer Feature composition model is preserved at full fidelity (first- and third-party Features,
devcontainer.metadata / customizations.aileron labels).
- README/docs (
docs/src/content/docs/development/sandbox-agent-images.md) document Docker as the only host prereq plus the escape hatch.
- Every sub-issue lands with unit tests for its contract; the matrix CI exercises the real build.
Spawned from feedback #1454.
Status (updated 2026-06-24) — ✅ ALL 6 SUB-ISSUES MERGED
The managed devcontainer toolchain is fully shipped on
main: verified Node fetch (clearsign-verifying real Node releases), per-arch resolution, pinned tools-lock, flag-gated wiring, Linux-gated real-build matrix CI, and the default flipped to managed. Docker is the only host prereq on Linux; the host-npxescape hatch remains for macOS/Windows.Path to done: #1528 first stalled on a Windows-only CRLF canonical-test failure (fixed via the missing
.gitattributes eol=lf). #1530's P0 (real-build matrix feasibility) was resolved by operator decision (b): Linux is the single required/blocking gate; macOS/Windows run non-blocking (continue-on-error) since hosted runners lack Docker. Implementing #1530 then exposed a real supply-chain bug in the merged #1526 verifier —VerifyAndParseused detached-signature verification, but Node publishesSHASUMS256.txt.ascas a clearsigned document, so no real Node release verified. Fixed toclearsign.Decode+ verify the embedded checksums, with a regression test routing Node's genuine release fixture through the production verifier (the gap the synthetic detached fixtures missed). #1531 then merged.Residual follow-ups (all closed)
--offline+ escape-hatch now rejects with an error naming both flags (shipped, withnode only/cli only/bothtests).main(it reads "consistency assert ... not a tamper guard"); the combined reject test shipped with cw-review-residual: plan findings for #1531 #1558; the cold-fetch→cache path is covered at thenodedistlayer (a toolchain-layer coldProvisiontest is infeasible against the real-Node lock-pin boundary).All sub-issues and residuals resolved — umbrella complete and closed.
Why
To stop redistributing Claude Code (#1451) the Claude agent image must be built on the user's machine. Today Feature-composed images are built by shelling
npx --yes @devcontainers/cli@0.87.0against the host Node: thedevcontainerCLIpackage-level var atinternal/sandbox/container/runtime.go:96, the CLI pinDevcontainerCLIVersionatinternal/sandbox/container/runtime.go:85, the build argv assembled indevcontainerCLIBuildArgsatinternal/sandbox/container/runtime.go:1069, and invoked aroundinternal/sandbox/container/runtime.go:373. A local build therefore adds a host Node/npx prerequisite on top of Docker, andnpxon native Windows is the rough spot (shim/PATHEXT/spaces).The operator's decision is option A: drive
devcontainer buildwith a Node +@devcontainers/clitoolchain Aileron downloads, verifies, caches, and selects per-platform itself, keeping Docker as the only host prereq and preserving the devcontainer Feature composition model at full fidelity (first- and third-party Features, thedevcontainer.metadata/customizations.aileroncapability labels).Option B (dockerized devcontainer CLI / docker-outside-of-docker) is rejected for now. Its only advantage — avoiding a from-scratch verified-fetch pipeline on a brand-critical surface — is moot, because Aileron already has a hardened download+verify+cache pipeline (
internal/cstore) to reuse. B's docker-socket plumbing is fully greenfield with its own cross-platform matrix (Docker Desktop vs rootless vs Windows) and a socket-mount threat surface that is off-brand. B would trade one cross-platform matrix (Node acquisition, now mostly reuse) for another (socket plumbing, fully greenfield).This is the keystone that unblocks #1451 and the multi-arch / bake-mcp follow-ons.
Reuse map (~60% is existing machinery)
internal/cstore: theFetcherinterface andHTTPFetcher(internal/cstore/install.go), theVerifierinterface (internal/cstore/verify.go:29), the content-addressed sha256 store under~/.aileron/store, and atomic-rename concurrency.Fetcher/Verifierare interfaces built for substitution.internal/launch/launcher.go:resolveSibling,siblingCandidates(.exesuffix),resolveReparsePoints(Windows junctions/symlinks),resolveShimTarget(Scoop shims), and the per-arch selection inresolveSandboxMCPBinary. This covers the Windows tail.agentdigeststool (internal/tools/agentdigests/main.go) +internal/sandbox/composition/agent-images.lock.json, plusDevcontainerCLIVersion.devcontainerCLIpackage-level var (internal/sandbox/container/runtime.go:96) exists precisely so resolution can be overridden.Greenfield (~40%)
Verifierfor Node's GPG-signedSHASUMS256.txt(Node does not use Aileron's Ed25519 keyring), substituted into the cstore pipeline shape; fetch Node distributions by version + GOOS/GOARCH from nodejs.org; unpack.warm/prefetch + offline path (cstore only short-circuits an already-stored hash today).Dependencies and sequence
The six sub-issues are tracked as native GitHub sub-issues. The approved sequence is:
devcontainer buildbehind the seam, flag-gated (depends on 1, 2, 3)devcontainer buildmatrix CI and flip the managed default (depends on 4)warm/prefetch + offline build path (depends on 4; build last)The managed path lands alongside host-npx behind a config/flag; the default flips to managed only once the matrix CI in sub-issue 5 is green.
Acceptance
devcontainer build(Tier 1, Features applied) succeeds on macOS (both arches), Windows, and Linux using only Aileron's managed Node +@devcontainers/cli— Docker is the only host prereq.SHASUMS256.txt, sha256-matched against a committed lock, and cached content-addressed; a tampered checksum or signature is rejected.--runtimeoverride) points at a user-provided Node / devcontainer CLI so no environment hard-blocks.t.Skip-when-unsupported.devcontainer.metadata/customizations.aileronlabels).docs/src/content/docs/development/sandbox-agent-images.md) document Docker as the only host prereq plus the escape hatch.Spawned from feedback #1454.