Digest-pin every Dockerfile base image
Epic: Security audit of development keys and container artifacts. This child = Architecture C8 (PRD R6).
## Original requirement (verbatim, epic body)
- "The installers run `curl | sh` with no checksum or signature. Images are tag-pinned with a `:latest` fallback, not pinned by digest, and not cosign-signed. Base images are unpinned."
- "3. Sign the images and pin them by digest, and have the installers check a checksum."
- "It should not break anything in prod or dev"
Operator decision D3: all base images (CE and `infra/local`) are digest-pinned with Dependabot bumps. This child owns base images only; signing and the compose pins are a sibling child.
## Approach (Architecture C8)
- Every `FROM` in `infra/community-edition/Dockerfile.*` and `infra/local/Dockerfile.*` becomes `image:tag@sha256:<index digest>`.
- Use the multi-arch index digest: the top-level `Digest` from `docker buildx imagetools inspect <image:tag>`. Never a platform digest, because api and web also build arm64.
- Keep the tag for readability.
- `infra/local/Dockerfile.jaahd-test`: `ARG UBUNTU_VERSION=26.04` stays the single home that `tools/jaahd_container_suite.sh:64-65` checks.
- Add `ARG UBUNTU_DIGEST=sha256:…` and `FROM ubuntu:${UBUNTU_VERSION}@${UBUNTU_DIGEST}`, with a comment to bump it together with `UBUNTU_VERSION`.
- `cgr.dev/chainguard/wolfi-base`: pin as `:latest@sha256:…`. The free tier publishes only `latest`.
- Dependabot already covers both directories (`.github/dependabot.yml:93,108`). Cite it; do not edit it.
## PRD R6 — Pin all base images by digest (A4; D3) — verbatim
- AC6.1 Every `FROM` in `infra/community-edition/Dockerfile.*` and `infra/local/Dockerfile.*` names `@sha256:` (tag kept for readability); none uses `:latest` alone.
- AC6.2 Dependabot (or the repo's existing bump mechanism) proposes digest bumps for these files.
- AC6.3 Dev/prod images still build and deploy.
Credentials & reach:
- Docker Hub / cgr.dev digest lookup (C8, C9) | Anonymous registry reads | verified — `docker buildx imagetools inspect` returned an index digest from h2 for `postgres:18`, `cgr.dev/chainguard/wolfi-base:latest`, `node:26-alpine`
- arm64 image builds for digest-pin oracle (C8) | buildx with arm64 emulation | verified — `docker buildx ls` on h2 lists a running builder with `linux/arm64`
- Quick/full gate, pytest, CE tree build + `check_tree`, installer harness, tree console build | Worktree only (`./dc`, `tools/community_edition/*.py` present) | verified — files present on h2; no credential needed
- PR checks / merge lane / Jaah Deploy run status | `jaah-forge` (pre-authenticated for the task) | verified — read verb `pr list` succeeded from h2
- Dev console boots, sign-in, Slack/Telegram settings, attachment upload (AC2.3, AC4.5, AC9.2) | `org/console/dev-login` (credential_get succeeded); environment: dev control plane | **deferred** to a node with reach. dev timed out from h2 (prod `app.jaah.ai` answered 200). Operator answer A5: a green Jaah Deploy is the gate, and the browser check runs on a node that reaches dev; a builder without reach records it deferred
Out of scope: PRD §5, verbatim:
- Do NOT change `infra/community-edition/node/install_clis.sh`'s vendor `curl https://claude.ai/install.sh | bash` (D8; deferred follow-up).
- Do NOT sign or digest-pin the cloud deploy images pushed by Jaah Deploy, nor add deploy-time verification (D3 chose CE + base images only).
- Do NOT add a CI/terraform guard over ECS env (D7 verify-only).
- Do NOT cosign-sign `SHA256SUMS` (D4).
- Do NOT remove Slack, Telegram, headscale, AWS, KMS, S3 or Bedrock from the cloud edition — the prune is CE-tree only.
- Do NOT change the session-secret / dev-seed / insecure-agent refusals' semantics or the `FIXTURE_APP_ENVS` set.
- Do NOT touch `infra/jaah/terraform` resources (only read them for R2).
- Do NOT change port binds, setup-loopback rule, cookie flags or rate limits (A6 positives).
- Architecture boundary: do not edit `infra/community-edition/compose.yml` or the release workflow (sibling child); do not edit `.github/dependabot.yml`; no base-image version bumps — pin the tag already in use.
Done means:
- AC6.1 — `grep -hn "^FROM" infra/community-edition/Dockerfile.* infra/local/Dockerfile.* | grep -v "@sha256:"` prints nothing (the ARG-parametrised ubuntu line resolves to a digest-pinned value); each pinned digest is a multi-arch index (`docker buildx imagetools inspect` shows a manifest list/index) — cite it per image.
- AC6.2 — the report cites `.github/dependabot.yml` docker entries covering both directories.
- AC6.3 — `./dc up` builds locally; the CE images build from the tree for linux/amd64 and linux/arm64 (`docker buildx build --platform linux/amd64,linux/arm64 -f infra/community-edition/Dockerfile.{api,web,node}` in the built tree); the PR's Jaah Deploy succeeds.
- AC9.1 `./dc check quick` green on the branch; merge lane `./dc check full` green.
- AC9.2 The PR's Jaah Deploy succeeds to dev; dev console loads and a user can sign in. (browser part deferred — dev unreachable from h2; operator A5: green Jaah Deploy is the gate)
Outcome
Digest-pinned every base-image FROM in infra/community-edition and infra/local (multi-arch index digests; ubuntu via ARG UBUNTU_DIGEST). Local and amd64+arm64 CE builds green; lane merged #7286 as ffbdc0350. Deploy superseded. Deferred: ubuntu digest bumped by hand. Memory: none applied.
Artifacts
Activity
- Draft
- Ready
- Runner started
- Runner ended (done)