phase: 24_dev_mtls_rollout
**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/`.
This commit is contained in:
+50
-3
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Local development server: start|stop|status|restart|logs
|
||||
# Local development server: start|stop|status|restart|logs|bundles
|
||||
#
|
||||
# ./scripts/dev-server.sh restart # reads .env.dev if present, runs on DASH_PORT
|
||||
#
|
||||
@@ -11,10 +11,54 @@ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
PIDFILE="$ROOT/data/dev-server.pid"
|
||||
LOG="$ROOT/data/logs/dev.log"
|
||||
# Phase 24: where the stored agent bundles are materialised for `install-agent.sh` to hand to a
|
||||
# host. Never committed — a bundle carries that host's private key — because `data/` is
|
||||
# gitignored, and the CA itself stays in the database (`certs.py`), never in this directory.
|
||||
CERTDIR="$ROOT/data/certs"
|
||||
mkdir -p "$ROOT/data/logs"
|
||||
|
||||
running() { [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; }
|
||||
|
||||
# The CA is durable in the database, so "generate certs during dev" means *export what the first
|
||||
# boot minted*: one <name>.bundle per configured host in gitignored data/certs/, ready to `scp`
|
||||
# to the host it belongs to. The names come out of HOST_TARGETS exactly the way `parse_targets`
|
||||
# reads it (space or comma separated `name=url`), so this script invents no host of its own and
|
||||
# re-parses nothing inside the exporter.
|
||||
#
|
||||
# A failure here is a warning line, never a start failure: the readiness probe proved the server
|
||||
# is up, and a bundle export that cannot sign in costs a dev convenience — not the dashboard, and
|
||||
# not the authority (the exporter only ever GETs the bundle route; it cannot mint).
|
||||
export_bundles() {
|
||||
local port="$1" names=() entry targets="${HOST_TARGETS:-}"
|
||||
for entry in ${targets//,/ }; do # space or comma separated, like config.parse_targets
|
||||
names+=("${entry%%=*}")
|
||||
done
|
||||
if [ ${#names[@]} -eq 0 ]; then
|
||||
echo " - no bundles exported (HOST_TARGETS unset)"
|
||||
return 0
|
||||
fi
|
||||
.venv/bin/python scripts/dev-certs-export.py \
|
||||
--url "http://127.0.0.1:${port}" \
|
||||
--password "${DASH_ADMIN_PASSWORD:-}" \
|
||||
--names "${names[@]}" \
|
||||
--out-dir "$CERTDIR" \
|
||||
|| echo " ! bundle export failed — check DASH_ADMIN_PASSWORD in .env.dev (the server is up and the CA is untouched)"
|
||||
}
|
||||
|
||||
# The exported-bundles block: names, sizes and modes, straight off the disk. `status` prints it,
|
||||
# because "is this host's bundle there yet?" is the question `status` gets asked; `bundles` is
|
||||
# the same block on its own.
|
||||
bundles() {
|
||||
echo "bundles exported ($CERTDIR):"
|
||||
local f found=0
|
||||
for f in "$CERTDIR"/*.bundle; do
|
||||
[ -e "$f" ] || continue
|
||||
found=1
|
||||
printf ' %s %s bytes mode 0%s\n' "${f##*/}" "$(wc -c <"$f")" "$(stat -c %a "$f")"
|
||||
done
|
||||
[ "$found" -eq 1 ] || echo " (none — restart the server, or run scripts/dev-certs-export.py)"
|
||||
}
|
||||
|
||||
start() {
|
||||
if running; then
|
||||
echo "already running (pid $(cat "$PIDFILE")) — http://127.0.0.1:${DASH_PORT:-8090}"
|
||||
@@ -33,6 +77,7 @@ start() {
|
||||
if running && curl -sf -o /dev/null "http://127.0.0.1:${PORT}/api/health"; then
|
||||
echo "started pid $(cat "$PIDFILE") — http://127.0.0.1:${PORT}"
|
||||
echo " sign in with DASH_ADMIN_PASSWORD from .env.dev"
|
||||
export_bundles "$PORT"
|
||||
else
|
||||
echo "FAILED to start; last lines of $LOG:"
|
||||
tail -20 "$LOG"
|
||||
@@ -80,7 +125,9 @@ case "${1:-start}" in
|
||||
| .venv/bin/python -c "import json,sys; d=json.load(sys.stdin); print('status:', d['status']); [print(' ', s['key'], 'ok' if s['ok'] else 'DOWN', (s['error'] or '')[:90]) for s in d['sources']]"
|
||||
else
|
||||
echo "not running"
|
||||
fi ;;
|
||||
fi
|
||||
bundles ;;
|
||||
bundles) bundles ;;
|
||||
logs) tail -n 40 "$LOG" ;;
|
||||
*) echo "usage: $0 start|stop|restart|status|logs"; exit 2 ;;
|
||||
*) echo "usage: $0 start|stop|restart|status|logs|bundles"; exit 2 ;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user