Skip to content

Aileron-managed devcontainer toolchain: drive devcontainer build with a self-managed Node + CLI so Docker is the only host prereq #1525

Description

@ALRubinger

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.

Sub-issue State PR
#1526 verified Node fetcher ✅ merged #1541
#1527 per-arch resolution ✅ merged #1538
#1528 Node tools-lock ✅ merged #1539
#1529 wire flag-gated ✅ merged #1549
#1530 matrix CI + flip default ✅ merged #1557
#1531 warm/offline ✅ merged #1559

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:

  1. Verified Node distribution fetcher (cstore reuse)
  2. Per-arch managed-binary resolution generalized to M architectures
  3. Node version+checksum tools lock alongside the CLI pin
  4. Wire the managed toolchain into devcontainer build behind the seam, flag-gated (depends on 1, 2, 3)
  5. Real-devcontainer build matrix CI and flip the managed default (depends on 4)
  6. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    milestoneTracks a major delivery milestone (e.g., Milestone v1, Milestone v2)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions