Problem
Per-project toolchains are baked into each project image:
ARG AIDC_TOOLCHAINS=""
RUN if [ -n "$AIDC_TOOLCHAINS" ]; then \
... \
go) sudo apt-get install -y --no-install-recommends golang-go && \
GOBIN=/home/vscode/.local/bin go install github.com/securego/gosec/v2/cmd/gosec@latest ;; \
rust) curl ... sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal --no-modify-path && \
"$HOME/.cargo/bin/cargo" install cargo-audit --locked ;; \
ruby) sudo apt-get install -y --no-install-recommends ruby-full && sudo gem install --no-document bundler-audit ;; \
java) sudo apt-get install -y --no-install-recommends default-jdk ;; \
...
Costs (measured on top of the 3.22 GB floor):
go: golang-go apt ~700 MB–1 GB, plus gosec@latest compiled from source (go build cache left in image)
rust: rustup stable toolchain ~1–1.5 GB, plus cargo-audit --locked compiled from source — leaves ~/.cargo/registry + target/ artifacts in the image
java: default-jdk ~400 MB
ruby: ruby-full ~300 MB
php: php-cli ~100 MB
A go+rust+java project → 5.5–6.5 GB image. Two projects with the same toolchain → two full copies of Go/Rust/JDK.
Proposal A (simplest, biggest win): shared read-only toolchain volume
Exactly the X-post idea — "mount snapshots of pre-verified tools as shared read-only memory, one copy serves 10,000 sandboxes":
- Create one shared Docker volume per toolchain (e.g.
aidc-toolchain-go, aidc-toolchain-rust, aidc-toolchain-java) — or a single aidc-toolchains volume with subdirs.
- Install the toolchain into the volume once (via a tiny helper image / init container, or
aidc tools install), then mount it read-only into every project container at the right path (/usr/local/go, $HOME/.cargo, etc.).
compose.yaml.tmpl adds - type: volume, source: aidc-toolchain-go, target: /usr/local/go, read_only: true per detected toolchain.
- PATH in the container includes the mounted bin dirs.
Result: one copy of Go/Rust/JDK serves all projects; per-project images stop growing by 1–2 GB per toolchain; toolchain upgrades/revocations happen in one place.
Proposal B (fallback, smaller change): prebuilt toolchain layer + cleanup
If volumes feel too invasive, at minimum:
- Install toolchains via prebuilt binaries instead of compiling from source (
gosec and cargo-audit publish release binaries — no go install/cargo install).
- Add
uv cache clean, go clean -modcache, remove ~/.cargo/registry + ~/.cargo/target after install, apt-get clean + rm -rf /var/lib/apt/lists/* inside the toolchain RUN.
Security notes
- Shared read-only mounts mean a compromised toolchain is revoked once (repopulate the volume) and stops everywhere — the "revoke a bad tool once, it stops everywhere" property.
- Read-only mount prevents agents from tampering with the toolchain.
Files
templates/devcontainer/Dockerfile.tmpl — toolchain block
templates/devcontainer/compose.yaml.tmpl — volume mounts
lib/aidc.sh — volume ensure/populate logic, PATH wiring
- New:
aidc tools subcommand (install/update/status of shared toolchain volumes)
Acceptance criteria
go version works in a go-project container with no golang-go in the image (docker exec shows /usr/local/go mounted read-only).
- Two projects with Go share one volume (docker volume ls shows one
aidc-toolchain-go).
cargo-audit runs without a Rust compile step in the project image.
Problem
Per-project toolchains are baked into each project image:
Costs (measured on top of the 3.22 GB floor):
go: golang-go apt ~700 MB–1 GB, plusgosec@latestcompiled from source (go build cache left in image)rust: rustup stable toolchain ~1–1.5 GB, pluscargo-audit --lockedcompiled from source — leaves~/.cargo/registry+target/artifacts in the imagejava: default-jdk ~400 MBruby: ruby-full ~300 MBphp: php-cli ~100 MBA go+rust+java project → 5.5–6.5 GB image. Two projects with the same toolchain → two full copies of Go/Rust/JDK.
Proposal A (simplest, biggest win): shared read-only toolchain volume
Exactly the X-post idea — "mount snapshots of pre-verified tools as shared read-only memory, one copy serves 10,000 sandboxes":
aidc-toolchain-go,aidc-toolchain-rust,aidc-toolchain-java) — or a singleaidc-toolchainsvolume with subdirs.aidc tools install), then mount it read-only into every project container at the right path (/usr/local/go,$HOME/.cargo, etc.).compose.yaml.tmpladds- type: volume, source: aidc-toolchain-go, target: /usr/local/go, read_only: trueper detected toolchain.Result: one copy of Go/Rust/JDK serves all projects; per-project images stop growing by 1–2 GB per toolchain; toolchain upgrades/revocations happen in one place.
Proposal B (fallback, smaller change): prebuilt toolchain layer + cleanup
If volumes feel too invasive, at minimum:
gosecandcargo-auditpublish release binaries — nogo install/cargo install).uv cache clean,go clean -modcache, remove~/.cargo/registry+~/.cargo/targetafter install,apt-get clean+rm -rf /var/lib/apt/lists/*inside the toolchain RUN.Security notes
Files
templates/devcontainer/Dockerfile.tmpl— toolchain blocktemplates/devcontainer/compose.yaml.tmpl— volume mountslib/aidc.sh— volume ensure/populate logic, PATH wiringaidc toolssubcommand (install/update/status of shared toolchain volumes)Acceptance criteria
go versionworks in a go-project container with no golang-go in the image (docker execshows /usr/local/go mounted read-only).aidc-toolchain-go).cargo-auditruns without a Rust compile step in the project image.