8.1 KiB
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:
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:
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.0and Python3.12.13. Python 3.12 is intentional because the harness declaresrequires-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 reported0.80.3. serverstarts/app/backend/dist/server.js;doctorroutes totht doctor;preprocessroutes to the future-facingtht preprocesscommand; explicittht ...and arbitrary CLI arguments route to the installedthtbinary.- The gate extension's
typeboxruntime dependency is installed from the harness lockfile.
Smoke and Diagnostic Results
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.
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.
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
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 --checkis clean.- Entrypoint processes use
exec, preserving container signal handling. - Backend production dependencies are pruned; TypeScript build tools remain in the build stage.
- The writable
/dataroot 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 preprocesscommand 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:
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:
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:
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:
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:
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:
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.