M13a_W5_VERIFICATION.md 7.1 KB

M13a W5 Verification

How to prove the W5 milestone (JWT gate on routerd / archiverd / deliverd-fcm / deliverd-telegram) is done.

What "W5 done" means

W3 shipped the JWT gate on admind and the shared authd.NewFromEnv() helper, but left the other four HTTP services out-of-scope (they were NATS-only consumers with no admin HTTP). W5 picks up the work: add a real admin route to each service, gate it with the same JWT middleware, ship tests and smoke coverage.

Concretely, the W5 exit criteria are:

  1. Each of cmd/routerd, cmd/archiverd, cmd/deliverd-fcm, cmd/deliverd-telegram has a wireAdminRoutes(mux, …, logger) helper that:
    • No-ops when BA_AUTHD_JWT_SECRET is unset (the LAN deploy path keeps working unchanged).
    • Wires the gate with ad.RequireAuth / ad.RequireRole when the secret is set.
  2. Each service exposes at least one real admin route:
    • routerd: GET /v1/admin/dedupe/state (read) + POST /v1/admin/dedupe/flush (destructive, admin role).
    • archiverd: POST /v1/admin/archiver/run (any authenticated user; the archiver is idempotent).
    • deliverd-fcm: GET /v1/admin/dlq + GET /v1/admin/dlq/{id} (channel=fcm, cross-channel safety: non-fcm row returns 404).
    • deliverd-telegram: same shape (channel=telegram).
  3. Unit tests cover: no-secret → 404 (route unregistered), with-secret + no-token → 401, role policy per service. routerd also has an end-to-end flush test against a real dedupe.Collapser. archiverd has a coalescing test (pre-filled channel → 202).
  4. go build ./..., go vet ./..., go test -count=1 ./... all clean.
  5. scripts/m13a_smoke.sh covers the W5 routes end-to-end against a running stack (checks 9–12).
  6. docker-compose.yml passes BA_AUTHD_JWT_SECRET to all four new services (sharing the same secret authd uses).

Step-by-step

1. Build + test

cd /root/.openclaw/workspace/broad-announce
go build ./...
go vet ./...
go test -count=1 ./cmd/routerd ./cmd/archiverd ./cmd/deliverd-fcm \
                  ./cmd/deliverd-telegram ./internal/dlq

Expected:

ok  	git3.techno-world.net/lrosales/broad-announce/cmd/routerd
ok  	git3.techno-world.net/lrosales/broad-announce/cmd/archiverd
ok  	git3.techno-world.net/lrosales/broad-announce/cmd/deliverd-fcm
ok  	git3.techno-world.net/lrosales/broad-announce/cmd/deliverd-telegram
?   	git3.techno-world.net/lrosales/broad-announce/internal/dlq  [no test files]

Test counts:

Package Tests Subtests Notes
cmd/routerd 5 4 incl. end-to-end Collapser flush
cmd/archiverd 5 3 incl. coalescing path
cmd/deliverd-fcm 3 3 any-authenticated-user can read
cmd/deliverd-telegram 3 3 any-authenticated-user can read
Total new (W5) 16 13

2. Run the gate unit tests in isolation

cd /root/.openclaw/workspace/broad-announce
go test -count=1 -v -run 'TestWireAdminRoutes|TestAdminDedupe|TestAdminRunNow|TestAdminRolePolicy|TestAdminDLQ' \
  ./cmd/routerd ./cmd/archiverd ./cmd/deliverd-fcm ./cmd/deliverd-telegram

Each of the four services should show:

  • TestWireAdminRoutes_NoSecret_Disabled — when BA_AUTHD_JWT_SECRET is unset, the admin routes return 404 (they're not registered). This is the LAN-deploy backward-compat path.
  • TestWireAdminRoutes_WithSecret_Gated — when the secret is set, the admin routes return 401 without a token.

routerd adds:

  • TestAdminDedupeState_Auth — an authenticated user can read the dedupe state JSON.
  • TestAdminRolePolicy_ViewerReadOnly_AdminCanFlush — viewer can read state (200), cannot flush (403); tenant_admin and super_admin can flush (200).
  • TestAdminDedupeFlush_DrainsPending — end-to-end: a pending collapse is observed, the handler is called, the pending count drops to 0.

archiverd adds:

  • TestAdminRunNow_Auth_Fires — an authenticated user can trigger a run; the trigger channel receives the signal.
  • TestAdminRunNow_Coalesces — a second concurrent request returns 202 with triggered=false, reason=already_pending.
  • TestAdminRolePolicy_ViewerCanTrigger — viewer, tenant_admin, super_admin can all fire (idempotent op).

deliverd-fcm and deliverd-telegram add:

  • TestAdminDLQ_AnyAuthenticatedUser_CanList — viewer, tenant_admin, super_admin can all read the per-channel DLQ (the gate passes; the handler then runs the SQL query).

3. Run the full M13a smoke (covers W5 end-to-end)

cd /root/.openclaw/workspace/broad-announce
docker compose up -d --build
bash scripts/bootstrap-super-admin.sh
bash scripts/m13a_smoke.sh

Expected: all 12 sections green, summary shows $PASS passed, $FAIL failed with 0 failed.

The W5-specific checks (9–12) verify:

  • 9. routerd /v1/admin/dedupe/state and /v1/admin/dedupe/flush:
    • No auth → 401.
    • With super_admin Bearer → 200.
  • 10. archiverd /v1/admin/archiver/run:
    • No auth → 401.
    • With Bearer → 200 (or 202 if a run is already in flight; both are accepted by the smoke check).
  • 11. deliverd-fcm /v1/admin/dlq:
    • No auth → 401.
    • With Bearer → 200, body contains "channel":"fcm".
  • 12. deliverd-telegram /v1/admin/dlq:
    • No auth → 401.
    • With Bearer → 200, body contains "channel":"telegram".

4. Backward-compat verification (LAN deploy)

To confirm the W5 gate doesn't break the LAN-only deploy (no BA_AUTHD_JWT_SECRET):

cd /root/.openclaw/workspace/broad-announce
# Comment out the BA_AUTHD_JWT_SECRET env on the four
# new services in docker-compose.yml (or just leave it
# empty), then:
docker compose up -d --build
curl -s -o /dev/null -w "%{http_code}\n" \
  http://127.0.0.1:8801/v1/admin/dedupe/state
# expect: 404 (route unregistered)

Notes

  • The internal/dlq/query.go package is the shared SQL helper used by both per-channel admin endpoints. The List query has a 30-day hard cap on created_at (same as admind's global DLQ endpoint). The Get query returns the full row including payload, with a pgx.ErrNoRows(nil, nil) idiom for idempotent callers.

  • The archiverd runLoop got a new triggerCh parameter (buffered to size 1). The loop now selects on {ctx.Done, tick.C, triggerCh}. The default cadence (BA_ARCHIVERD_RUN_EVERY_SECONDS, 3600s) is unchanged; the admin route is a side door that fires a run on demand.

  • The cross-channel safety net in the per-channel DLQ endpoints (row.Channel != "fcm" → 404) prevents a deliverd-fcm from accidentally exposing telegram-channel DLQ rows. The global /v1/dlq in admind doesn't need this; it serves the full cross-channel view.

See also

  • M13a_PLAN.md § W5 — the design rationale and exit criteria.
  • scripts/m13a_smoke.sh — the E2E smoke, including the W5 checks 9–12.
  • internal/authd/middleware.goRequireAuth and RequireRole (W2). The W5 admin routes are thin wrappers around these.