Skip to content

Image size: share Go/Rust/Java toolchains via a shared read-only volume instead of baking into every image #9

Description

@ai-anant

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":

  1. 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.
  2. 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.).
  3. compose.yaml.tmpl adds - type: volume, source: aidc-toolchain-go, target: /usr/local/go, read_only: true per detected toolchain.
  4. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions