# Container image build inputs

Bot-bottle's supported images are intended to rebuild from the same declared
inputs on Linux amd64 and arm64. The repository enforces four layers of
immutability:

- Python, Node, and nested-container Docker CLI base images are required,
  defaultless Docker build arguments.
  Their version-qualified tags and multi-platform OCI index digests live
  together in `image-build-args.json`; every supported builder reads that
  file and passes only the arguments its Dockerfile declares.
- Debian packages resolve from the same dated `snapshot.debian.org` archive in
  every image that runs `apt-get install`. The snapshot endpoint uses HTTP so a
  fresh image does not need a host-specific TLS interception CA; APT still
  authenticates the repository metadata and package hashes with Debian's
  signed Release files.
- Gateway Python dependencies install from `requirements.gateway.lock` with
  `pip --require-hashes`; provider npm packages install with `npm ci` from
  committed lockfiles.
- The Codex standalone archive is fetched from the exact `CODEX_VERSION`
  release and checked against the committed upstream
  `codex-package_SHA256SUMS` before extraction.
  The standalone layout is retained because Codex remote control requires it.

`Dockerfile.orchestrator.fc` is the one intentionally dynamic `FROM`. It has no
default value: the Firecracker build coordinator resolves the freshly built
orchestrator's local `sha256:` image ID, creates a local tag containing the
complete ID, verifies that tag resolves to the same ID, and supplies the
content-derived reference as `ORCHESTRATOR_BASE_IMAGE`. This accommodates
BuildKit, which treats a bare image ID in `FROM` as a registry repository.

## Refreshing inputs

Make refreshes on a feature branch and review them like an application
dependency update:

1. For a Python or Node base update, select a version-qualified tag and update
   its one digest-pinned value in `image-build-args.json`. Confirm the index
   still contains `linux/amd64` and `linux/arm64`.
2. For Debian packages, advance `DEBIAN_SNAPSHOT` to one fixed UTC timestamp in
   every Dockerfile that uses apt. Do not use the moving Debian mirrors.
3. Change direct Python versions in `requirements.gateway.in`, direct npm
   versions in the provider's `package.json`, or `CODEX_VERSION` in the Codex
   Dockerfile. Direct versions must be exact—no ranges, dist-tags, or
   unversioned package names.
4. Push the feature branch or manually run the `refresh-image-locks` workflow.
   It regenerates the four derived files with the image's exact Python and Node
   versions and uploads them as the `image-input-locks` artifact. It never
   writes to the branch, so a refresh cannot race with developer pushes.
5. Download the artifact, replace the four committed files, review the lock
   diff, and let both normal CI and `image-input-builds` pass.
   The latter checks both base architectures, builds every supported image,
   exercises the exact local Firecracker base-ID handoff, and runs each
   provider CLI's version smoke test.

Run the fast policy check locally at any point:

```sh
python3 scripts/check_image_inputs.py
```

The check rejects mutable or `latest` bases, moving apt repositories,
network-to-shell installers, unlocked npm/Pi installs, non-exact direct
dependencies, missing npm integrity values, and unhashed gateway Python
requirements.

Image signing and attestation are tracked separately in issue #339; this policy
covers deterministic and integrity-checked build inputs.
