# Container Packaging Task 3 Report ## Status Implemented the multi-stage core application image, non-root runtime, pinned Pi installation, container entrypoint, context exclusions, and an in-image health smoke test. ## TDD / Build Evidence Initial RED: ```text docker build -f docker/core.Dockerfile -t thothii-core:test . ERROR: failed to build: resolve : lstat docker: no such file or directory ``` The first sandboxed attempt could not access the Docker socket; the authorized rerun reached the builder and failed for the expected reason: the Dockerfile did not exist. GREEN build: ```text sh -n docker/core-entrypoint.sh docker/smoke/core-smoke.sh docker build --progress=plain -f docker/core.Dockerfile -t thothii-core:test . ``` Result: shell syntax exited 0; Docker build exited 0. A final rebuild after tightening `.dockerignore` also exited 0 and transferred only 17.60 kB of changed context (the initial clean build transferred 1.02 MB). ## Runtime and Entrypoints - Runtime user is `10001:10001` (`thoth`), never root. - Runtime contains Node `v22.19.0` and Python `3.12.13`. Python 3.12 is intentional because the harness declares `requires-python = ">=3.12"` and also satisfies the deployment floor of 3.11+. - Pi is installed exactly as `@earendil-works/pi-coding-agent@0.80.3`; its build-time and runtime version probes both reported `0.80.3`. - `server` starts `/app/backend/dist/server.js`; `doctor` routes to `tht doctor`; `preprocess` routes to the future-facing `tht preprocess` command; explicit `tht ...` and arbitrary CLI arguments route to the installed `tht` binary. - The gate extension's `typebox` runtime dependency is installed from the harness lockfile. ## Smoke and Diagnostic Results ```text docker run --rm thothii-core:test doctor config: error - configuration is invalid or unreadable data_root: ok ``` Result: expected exit 1 for absent mounted workspace configuration, with no traceback and no secret-bearing validation detail. ```text docker run --rm --entrypoint /app/docker/smoke/core-smoke.sh thothii-core:test backend listening on http://127.0.0.1:8787 v22.19.0 Python 3.12.13 core smoke: ok ``` Result: exit 0. The script asserted non-root execution, `tht --help`, `pi --version`, runtime version floors, and `GET /health` through curl. Fastify's returned display address was loopback; the inspected container environment is `HOST=0.0.0.0`, and the compiled server passes that value to `app.listen`. ```text docker run --rm thothii-core:test tht --version 0.1.0 ``` Result: arbitrary `tht` entrypoint exited 0. An explicit runtime assertion checked UID 10001, exact Node and Pi versions, Python 3.11+, and the absence of `/app/harness/.env` and `/app/harness/workspaces`; it exited 0. ## Image Size and Containment Inspection ```text docker image inspect thothii-core:test --format '{{.Size}} {{json .Config.User}} {{json .Config.Env}}' 221419008 "10001:10001" [...runtime paths and version metadata only...] ``` Image size: **221,419,008 bytes** (about 211.2 MiB). `docker history --no-trunc thothii-core:test` was inspected. It contains only Dockerfile commands, the pinned public package name/version, base-image metadata, and non-sensitive runtime variables; no credentials or customer paths were found. An in-image filename scan found only `/app/harness/.pi/settings.json` among `.env`, key/certificate, and settings-name candidates; that tracked Pi file contains theme/startup preferences, not secrets. The build asserts `.env` and workspace directories are absent. `.dockerignore` excludes VCS/agent state, all environment files except examples, package-manager credential files, SSH/private-key and certificate formats, local virtualenvs/node_modules/caches, backend runtime data, customer workspaces, sessions, artifacts, indexes, corpus, and deployment mount content. ## Self-review - `git diff --check` is clean. - Entrypoint processes use `exec`, preserving container signal handling. - Backend production dependencies are pruned; TypeScript build tools remain in the build stage. - The writable `/data` root is owned by UID 10001; application payload remains root-owned and read-only to the runtime user. - CA certificates and curl are present for HTTPS integrations and health probing. - No existing source, customer workspace, secret, or unrelated progress-ledger change is included in the task commit. ## Concerns - The `tht preprocess` command is deliberately a future-facing routing contract; its CLI group is scheduled in the Evidence/preprocessing plan and is not implemented in the current harness. - Python dependencies are range-resolved because the existing harness has no Python lockfile. The Pi package, Node runtime, and package-lock-backed Node dependency sets are pinned/reproducible. - The image was built and smoked on Docker Desktop arm64. The chosen official multi-arch base images and Pi package are architecture-neutral at the package level, but amd64 still needs a CI build/smoke before being advertised as verified. ## Reproducibility Review Fix The original image pinned Pi's direct version in the Dockerfile but resolved its transitives at build time, and pip resolved all harness dependencies from ranges. Both paths now consume committed locks. ### Lock generation Pi uses the minimal `docker/pi-runtime/package.json` and its committed npm v3 lock. It was generated with: ```text npm install --package-lock-only --ignore-scripts --no-audit --no-fund \ --prefix docker/pi-runtime ``` The package manifest specifies exact `@earendil-works/pi-coding-agent` version `0.80.3`; a lock inspection confirmed that same resolved package version. Docker installs it with: ```text npm ci --omit=dev --ignore-scripts --no-audit --no-fund ``` The Python lock was generated directly from the harness production metadata plus one explicit, pinned PEP 517 build-backend input—not from a host `pip freeze`: ```text uv pip compile harness/pyproject.toml docker/python-runtime/build-requirements.in \ --universal \ --python-version 3.12 \ --no-emit-package tht \ --generate-hashes \ --custom-compile-command \ 'uv pip compile harness/pyproject.toml docker/python-runtime/build-requirements.in --universal --python-version 3.12 --no-emit-package tht --generate-hashes --output-file docker/python-runtime/requirements.lock' \ --output-file docker/python-runtime/requirements.lock ``` `pytest`, `ruff`, and `testcontainers` are absent. All production direct and transitive packages are exact and hashed. `setuptools==80.9.0` is explicit so the local harness install can use `--no-build-isolation` without an unpinned build-time resolution. Refresh instructions are in `docker/LOCKS.md`. ### No-cache rebuild and verification Final build command: ```text docker build --no-cache -f docker/core.Dockerfile -t thothii-core:test . ``` Result: exit 0. The logs showed Pi `0.80.3`, Node `v22.19.0`, a hash-enforced Python dependency install, explicit `setuptools==80.9.0`, and a non-isolated local `tht` wheel build. No isolated build-dependency download occurred. Fresh runtime checks: ```text docker run --rm --entrypoint /app/docker/smoke/core-smoke.sh thothii-core:test backend listening on http://127.0.0.1:8787 v22.19.0 Python 3.12.13 core smoke: ok docker run --rm thothii-core:test tht --version 0.1.0 /opt/venv/bin/pip check No broken requirements found. ``` An in-container package inspection reconfirmed Pi `0.80.3`. Non-root UID, runtime version floors, doctor's expected concise exit 1/no traceback, `/health`, and arbitrary `tht` routing all passed. The full filename containment scan found no `.env`, PEM, private-key, P12, or PFX file in `/app`; `/app/harness/workspaces` remains absent. Image environment and `docker history --no-trunc` were re-inspected and contain only public package/build commands and non-sensitive runtime metadata. Final locked image size: ```text 220003986 10001:10001 ``` That is **220,003,986 bytes** (about 209.8 MiB), 1,415,022 bytes smaller than the original image. Remaining concern: the universal lock is resolved for Python 3.12 and includes hashes/markers for all supported platforms, but only Linux arm64 has been built and smoked locally; amd64 remains a CI verification gate.