207 lines
8.1 KiB
Markdown
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.
|