Container and deployment scenarios have never been executed (Dockerfile is statically verified only) #86

Closed
opened 2026-09-23 06:19:43 +00:00 by yuris · 0 comments
Owner

Context

Dockerfile and the flake's container target were written and reviewed in the ops slice, but the image has never been built or run. The Docker daemon was unavailable then, and it still is: the CLI exists at /usr/local/bin/docker, but the socket is missing (~/.docker/run/docker.sock: connect: no such file or directory).

So the release artifact is unverified. What was actually checked is the Dockerfile's content and the derivation's evaluation — not that the image builds, starts, serves, or survives the deployment scenarios it claims.

The Dockerfile, for reference:

FROM nixos/nix:2.24.9 AS builder
FROM scratch AS runtime
USER 10001:10001
VOLUME ["/var/lib/life-service"]
EXPOSE 3000
ENTRYPOINT ["/usr/local/bin/life"]
CMD ["serve", "--config", "/etc/life/life.toml"]

The liveness/readiness surface is /health/live and /health/ready.

Why it matters

docs/technical/verification.md's deployment scenarios are the contract for shipping, and several are container-shaped rather than library-shaped, so no unit test can stand in for them:

Scenario What it requires
D06 At most one owner: a second writer on the same local volume is rejected by the lock; no network-filesystem WAL deployment
D07 Readiness distinguishes a write/storage failure from an optional backend outage; liveness unaffected
D09 Migration-before-serving; an unsupported (old-binary) schema is rejected rather than served
D10 An online SQLite snapshot taken while WAL holds committed data includes that state; copying the live main file alone is rejected as a procedure
D11/D12 Encrypted off-host backup restored against a differently timed Radicale backup passes integrity/foreign-key checks; corrupt snapshot/missing manifest aborts rather than reporting false complete history

What to build

  1. Build the image from the locked derivation and record the real output (build log, image size, digest).
  2. Run it as documented: non-root uid 10001, config mounted read-only, secret files mounted, VOLUME for state, EXPOSE 3000.
  3. Exercise the scenarios above that are locally reachable — D06, D07, D09, D10 at minimum, plus a /health/ready check and a real mutation round-trip through the CLI against the container.
  4. For any scenario that genuinely needs a second host or a second backend (off-host encrypted backup, differently timed Radicale restore), say so explicitly and state exactly what remains unproven instead of implying coverage.

Acceptance criteria

  • The image builds reproducibly from the committed lockfile; build output and image digest recorded in the PR.
  • The container starts as uid 10001, serves /health/live and /health/ready, and completes a real create/read/update round-trip through the CLI.
  • D06: a second process on the same state volume is rejected by the lock, and the rejection is observable in the log/exit code.
  • D07: with the journal unwritable, readiness reports a storage failure while liveness stays green; with only the CalDAV backend down, the two are distinguished.
  • D09: the container migrates before serving, and an unsupported schema version is refused rather than served.
  • D10: the snapshot procedure includes committed WAL state, and the "copy the live main file" shortcut is rejected.
  • Every scenario that could not be exercised locally is named with the reason, in the PR body — no silent omissions.
  • Release notes state the verified/unverified split.

Blocked by

Needs a running Docker daemon on the build host (start Docker Desktop). No code dependency. Related: the ops slice's Dockerfile, #36 (backup/DR rehearsal, which ran outside a container).

## Context Dockerfile and the flake's container target were written and reviewed in the ops slice, but the image has **never been built or run**. The Docker daemon was unavailable then, and it still is: the CLI exists at /usr/local/bin/docker, but the socket is missing (~/.docker/run/docker.sock: connect: no such file or directory). So the release artifact is unverified. What was actually checked is the Dockerfile's content and the derivation's evaluation — not that the image builds, starts, serves, or survives the deployment scenarios it claims. The Dockerfile, for reference: ``` FROM nixos/nix:2.24.9 AS builder FROM scratch AS runtime USER 10001:10001 VOLUME ["/var/lib/life-service"] EXPOSE 3000 ENTRYPOINT ["/usr/local/bin/life"] CMD ["serve", "--config", "/etc/life/life.toml"] ``` The liveness/readiness surface is /health/live and /health/ready. ## Why it matters docs/technical/verification.md's deployment scenarios are the contract for shipping, and several are **container-shaped** rather than library-shaped, so no unit test can stand in for them: | Scenario | What it requires | |---|---| | D06 | At most one owner: a second writer on the same local volume is rejected by the lock; no network-filesystem WAL deployment | | D07 | Readiness distinguishes a write/storage failure from an optional backend outage; liveness unaffected | | D09 | Migration-before-serving; an unsupported (old-binary) schema is rejected rather than served | | D10 | An online SQLite snapshot taken while WAL holds committed data includes that state; copying the live main file alone is rejected as a procedure | | D11/D12 | Encrypted off-host backup restored against a differently timed Radicale backup passes integrity/foreign-key checks; corrupt snapshot/missing manifest aborts rather than reporting false complete history | ## What to build 1. Build the image from the locked derivation and record the real output (build log, image size, digest). 2. Run it as documented: non-root uid 10001, config mounted read-only, secret files mounted, VOLUME for state, EXPOSE 3000. 3. Exercise the scenarios above that are locally reachable — D06, D07, D09, D10 at minimum, plus a /health/ready check and a real mutation round-trip through the CLI against the container. 4. For any scenario that genuinely needs a second host or a second backend (off-host encrypted backup, differently timed Radicale restore), say so explicitly and state exactly what remains unproven instead of implying coverage. ## Acceptance criteria - [ ] The image builds reproducibly from the committed lockfile; build output and image digest recorded in the PR. - [ ] The container starts as uid 10001, serves /health/live and /health/ready, and completes a real create/read/update round-trip through the CLI. - [ ] D06: a second process on the same state volume is rejected by the lock, and the rejection is observable in the log/exit code. - [ ] D07: with the journal unwritable, readiness reports a storage failure while liveness stays green; with only the CalDAV backend down, the two are distinguished. - [ ] D09: the container migrates before serving, and an unsupported schema version is refused rather than served. - [ ] D10: the snapshot procedure includes committed WAL state, and the "copy the live main file" shortcut is rejected. - [ ] Every scenario that could not be exercised locally is named with the reason, in the PR body — no silent omissions. - [ ] Release notes state the verified/unverified split. ## Blocked by Needs a running Docker daemon on the build host (start Docker Desktop). No code dependency. Related: the ops slice's Dockerfile, #36 (backup/DR rehearsal, which ran outside a container).
yuris closed this issue 2026-09-23 10:54:21 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/life-service#86
No description provided.