**Phase 24 final verification — all green**
- **Verified (read-only re-run, 62/62 PASS)** via `data/logs/verify-24-mtls.py`: nine sources `ok`; `/api/certs` all three `active` with `deployed_bundle_rev == bundle_rev == 2`; `agent.tls: true` + `agent.version 1.1.0` on every host; driveripper 16/16 drives live; gateway absent from `/disks` but `disks:gateway ok`; all served target URLs `https://`, no userinfo; pre-flip backup present; no transport/cert alert.
- **Criterion 1 (export):** `data/certs/{driveripper,deskwork,gateway}.bundle` re-checked byte-identical (sha256) to `GET /api/certs/<name>/bundle`, mode `0600`, under gitignored `data/`; live arms re-probed (issued→file, unknown/`issued:false`→one-line note + no file, wrong password→exit 2, nothing written).
- **Criterion 2 (`--fetch-bundle`):** 54 sandbox pins in `tests/test_deploy_scripts.py` pass — byte-identical `0640` install + TLS unit lines, refusals (unissued / 401 / non-bundle body / dead dashboard) before `need_root` with no write, token absent from token file, unit, stdout and sudoers hint.
- **Criterion 3–4:** `./scripts/dev-server.sh status` nine `ok` + bundles block; screenshots `data/logs/24-task04-{certs,disks,system}.png` exist.
- **Criterion 7:** no behavior change — `agent/` untouched, `src/` diff is a `certs.ensure_certs` docstring only; token-mode, revocation-before-dial and bundle-route tests green.
- **Tests/lint:** `bash .agents/validate.sh` → **EXIT=0**; `uv run pytest --cov --cov-fail-under=94` → **1542 passed, 15 skipped, coverage 98.94%**; `ruff check src tests agent` → All checks passed; `pyright` → 0 errors; `node --check` clean; `uv run pytest -m live --live` → **11 passed**.
- **Docs:** README.md (all 1144 lines) + AGENTS.md reviewed against the live project — every config.py env var is in README and both templates, route set / severity thresholds / test counts / coverage figure match, all repo paths, cited test names and `Dash.*`/`DashCharts.*` helpers resolve, phase-24 doc pins green. **Verified consistent — no changes needed.**
- **Notable:** left `fastapi 0.153.0 → 0.158.0` (validate reports it as advisory; no dependency was touched, rule 8 conditions the check on deps moving). Killed two abandoned `/tmp/fbtest` loopback stub processes left by task 02's manual probe; dev server unaffected.
- **Next pending phase:** `.agents/phases/todo/25_masthead_control_placement/`.
3.5 KiB
3.5 KiB
Task 04 — The fleet is mTLS: the end state, verified
Phase: 24_dev_mtls_rollout · Source: TODO.md:3 — "…so we never need non-mtls servers."
Story: n/a
Objective
The end state is proven read-only, the way phase 15's task-05 final pass proved its composition: the dashboard dials all three agents with its client certificate, the issued-vs-deployed revisions agree, nothing non-mTLS is left in the config, and the things the bundle install must not have broken (driveripper's 16 SMART reads, deskwork's GPU row) still work. No live revocation is performed — the revocation wiring is phase 15's e2e proof (data/logs/verify-15-tls-e2e.py), and a live revoke of a working fleet is a change, not a check.
Work
./scripts/dev-server.sh status— the nine-source tableok(host:driveripper,host:deskwork,host:gateway,disks:driveripper,disks:deskwork,disks:gateway,nut,llm:gufo,llm:vllm);disks:gatewayisokwith the page hiding gateway (phase 22's gate) — the job healthy, the page scoped.- Signed-in
GET /api/certs: all three rowsstatus: "active",deployed_bundle_rev == bundle_rev(the "install the new bundle" sentence is gone for every host),agentblock from thesystempayload:agent.tls: true+agent.bundle_revpresent on all three (the task-03 passthrough — a token-mode snapshot grows nothing, so the fields' presence is the mTLS proof, rule 2's shape). - Signed-in
GET /api/system/snapshot+GET /api/disks/snapshot: theagentblocks on all three hosts; gateway's row absent from/disks(phase 22); driveripper's 16 drives with live SMART numbers (the--forceunit rewrite is the gap-10 shape — asmart: nullrow here fails the task); deskwork'snvme0n1row and its GPU card on/system(the 1.1.0 agent file's reads intact). - The config:
grepof.env.dev— nohttp://inHOST_TARGETS, no@userinfo in any target URL;data/env-backup/holds the pre-flip copy (the rollback path exists on disk). - The negative control, without touching the fleet:
data/logs/verify-15-tls-e2e.py's transcript (phase 15) is the standing proof that a revoked serial is refused on/healthtoo; this task re-runs none of it live — it names the transcript as the control and checks only thattests/test_collectors.py's revocation-before-dial pin andtests/test_api.py'sbuild_hubwiring pin are green (they run in the suite). - Screenshots (invariant 10) in
data/logs/:/certs(the three active rows with matching revs),/disks(the two visible hosts, driveripper's 16 live rows),/system(deskwork's GPU card) — the "the dashboard says all three are mTLS" proof is a screenshot, not a curl.
Testing & Quality
- Coverage: n/a (verification task)
- The read-only rule: no
POSTto the fleet, noupsdcommand, no revocation, no unit change — the task reads the fleet and screenshots it
Completion Criteria
- nine sources
ok;deployed_bundle_rev == bundle_revandagent.tls: truefor all three hosts (pinned by the API checks, shown by the screenshots) .env.devhas nohttp://agent URL and no userinfo; the pre-flip backup exists- driveripper's 16 SMART reads and deskwork's GPU card are live in the served payloads
- screenshots of
/certs,/disks,/systemindata/logs/ - the revocation pins (
tests/test_collectors.py,tests/test_api.py) green in the suite - the full suite +
bash .agents/validate.shgreen; AGENTS.md's fleet record and gap 11 updated by the phase's Documentation & Consistency pass to the mTLS end state