Files
ThothII/.superpowers/sdd/container-task-3-report.md
T

207 lines
8.1 KiB
Markdown

# 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.