Files
arah2/.planning/phases/07-hardening-private-launch/07-03-SUMMARY.md
araharah-executor dea6261deb docs(07-03): complete the consolidated privacy-audit plan — SUMMARY
- 12-vector TDD suite, 4 source gates + bundle gate + 2 live probes, the
  0/1/2 exit contract, and docs/PRIVACY-AUDIT.md signed-record scaffold
- PRIVACY AUDIT CLEAN — 5 gate(s) passed, 0 violations on the audited tree
- baselines intact: bot 125/125, web 165/165
2026-10-25 04:41:12 +08:00

10 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
07-hardening-private-launch 03 testing
privacy-audit
grep-gates
comment-stripping
kv-ttl
zero-telemetry
allowlist
wrangler
cloudflare-kv
curl-probe
phase provides
03-mini-app-shell-geolocation the memory-only client law + the canonical comment-stripper (web/test/no-storage.test.mjs, D-07 origin)
phase provides
02-bot-skeleton-initdata-security the PG1/PG2 TTL-only KV write law (bot/test/privacy.test.mjs) and the D-14 category-only log vocabulary (bot/src/webhook.ts)
phase provides
05/06 routing + guidance the route-privacy RP1/RP2 allowlist style and the TTL key families the audit probes (bot/src/kv/*)
The ONE consolidated audit CLI: npm run privacy-audit (scripts/privacy-audit.mjs) — 5 static gates + 2 --live probes, exit codes 0/1/2 (never a silent pass)
scripts/lib/source-gates.mjs — storage-api / kv-ttl / log-vocabulary / runtime-hosts gates extended repo-wide over ALL five src trees
scripts/lib/bundle-gates.mjs — PRODUCTION web/dist telemetry scan (analytics/beacon tokens, positive host allowlist, deep-link crypto, storage tokens)
scripts/lib/cloudflare-probes.mjs — live kv-expiry (list_complete/cursor/expiration parsing) + live asset-headers probes, degrading to notRun without credentials
docs/PRIVACY-AUDIT.md — the signed written audit record citing the ACTUAL run evidence (D-01), with a human signature block for the launch gate
07-04 (budget-audit shares the _headers/live-probe surface)
07-05 (limitations doc)
07-06 (launch gate — the privacy-audit-signed checklist leg)
any fork onboarding
added patterns
Gate-as-function returning {id, pass, evidence, violations} (07-RESEARCH Pattern 4 shape)
Canonical comment-stripped source introspection with documented prefer-fail adjudications (theme-audit 'Scope adjudications' precedent)
Positive host allowlist with the inventory PRINTED, never assumed (A4)
notRun as the exit-2 class: a skipped probe is never a pass (T-07-16)
PRIVACY_AUDIT_ROOT env override — cwd-independent gates, fork/test injectability
created modified
scripts/privacy-audit.mjs
scripts/lib/source-gates.mjs
scripts/lib/bundle-gates.mjs
scripts/lib/cloudflare-probes.mjs
scripts/test/privacy-audit.test.mjs
docs/PRIVACY-AUDIT.md
package.json (the root "privacy-audit" script)
Bundle host scan adjudication: MapLibre's own static-text URLs (maplibre.org, wiki.openstreetmap.org, github.com diagnostics, maplibre.invalid) + our ODbL license strings (opendatacommons.org) form a DOCUMENTED static-text class alongside the 2-host fetch allowlist — each host enumerated with its reason; any other host fails
W3C namespace identifiers (svg/xhtml) excepted as spec constants, never fetched — documented in both gate files
Line-numbered gates scan raw lines with only the // tail removed (honest line numbers); block-comment prose mentioning a scanned token fails prefer-fail
Asset-headers probe reads HTTP status BEFORE interpreting headers — a stale-deploy 404 is deployment drift (notRun, exit-2 class), never a cache violation
The audit suite is a hand-rolled 12-vector TDD file (scripts/test/privacy-audit.test.mjs) with fixture-root injection, proving each gate can FAIL — a gate that cannot fail is decoration
PRIVACY AUDIT CLEAN/FAILED/NOT RUN — the consolidated audit's summary vocabulary + 0/1/2 exit contract
Gates are cwd-INDEPENDENT (module-URL root) so a stray subdirectory run cannot false-pass
PX-04
9min 2026-10-24

Phase 7 Plan 03: Consolidated privacy audit + signed record Summary

One consolidated npm run privacy-audit CLI — 5 static gates over all five src trees + the production bundle, 2 credential-gated live probes, honest 0/1/2 exit codes — plus docs/PRIVACY-AUDIT.md, the signed audit record citing the real run's evidence (D-01/D-02).

Performance

  • Duration: 9 min (20:32Z → 20:41Z)
  • Started: 2026-10-24T20:32:07Z
  • Completed: 2026-10-24T20:41:00Z
  • Tasks: 3 (TDD RED→GREEN)
  • Files modified: 7 (6 created, 1 modified)

Accomplishments

  • The repo's gate lineage consolidated, not duplicated: the phase-3 memory-only law, the PG1 same-line TTL law, the D-14 category-only log law, and the bundle laws are now enforced REPO-WIDE by one tool — the existing unit-level suites stay as the pins
  • All 5 static gates PASS on the audited tree (commit 9c55e9a): 72 files scanned, 4 KV writes all TTL-named, 28 log call sites category-verified, host inventories printed (source + bundle), ZERO analytics/beacon/crypto/storage tokens in web/dist
  • docs/PRIVACY-AUDIT.md carries the ACTUAL evidence strings from the run, the four KV-family storage inventory, the log vocabulary, the runtime-network inventory with D-02's zero-telemetry posture, and the blank human signature block (D-09's launch gate signs it)
  • Live probes verified honest-by-construction: without credentials both return notRun and the CLI exits 2 — never a silent pass (T-07-16)

task Commits

Each task was committed atomically (TDD: RED gate commit → GREEN feat commits):

  1. task 1: the source-gates library — 87fcb28 (test: RED — 12-vector suite) + 430993b (feat)
  2. task 2: bundle-gates + cloudflare-probes — b959e23 (feat)
  3. task 3: the CLI + npm script + signed record — 9c55e9a (feat) + 8229e36 (docs)

Files Created/Modified

  • scripts/lib/source-gates.mjs — runStorageGate / runKvTtlGate / runLogVocabularyGate / runRuntimeHostGate over web/bot/routing/search/theme src
  • scripts/lib/bundle-gates.mjs — runBundleTelemetryGate (PRODUCTION web/dist ONLY; dist-harness forbidden)
  • scripts/lib/cloudflare-probes.mjs — runKvExpiryProbe / runAssetHeaderProbe / probeUnavailable (namespace id read from bot/wrangler.jsonc at runtime)
  • scripts/privacy-audit.mjs — the orchestrator: Pattern-4 ordering, per-gate evidence lines, CLEAN/FAILED/NOT RUN summaries, process.exit 0/1/2
  • scripts/test/privacy-audit.test.mjs — the 12-vector TDD suite (SA1-5, BG1-2, CP1-2, AU1-3)
  • package.json — root "privacy-audit": "node scripts/privacy-audit.mjs"
  • docs/PRIVACY-AUDIT.md — the signed audit record (D-01)

Decisions Made

  • Bundle static-text adjudication: the plan's 2-host allowlist fails on MapLibre's own embedded attribution/diagnostic URLs in the shipped bundle (they are library constants and our dataset's ODbL metadata — never fetched). Followed the repo's own "Scope adjudications (documented, deliberate)" precedent (theme/scripts/audit.mjs): an explicitly enumerated static-text class, each host with its reason, full inventory printed as evidence. Any host outside both lists fails.
  • Header probe honesty: the first live run exposed the probe interpreting a 404 as "no cache-control" — the probe now reads HTTP status first and treats origin/build mismatch as deployment drift (exit-2 class). The immutable /assets/* policy ASSERTION belongs to 07-04's budget-audit (its _headers file); this probe quotes, never interprets.
  • Test injectability: gates accept an opts-only roots/dist override (production callers pass nothing), letting the suite prove each gate FAILS on synthetic violations in temp fixtures.
  • cwd-independent root with the PRIVACY_AUDIT_ROOT override — a stray subdirectory run must not scan the wrong tree and false-pass.

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] Asset-header probe misread a stale-deploy 404 as a cache violation

  • Found during: task 3 (first --live run)
  • Issue: the probe fetched the local dist's hashed asset from the deployed origin without checking HTTP status; a 404 printed "no cache-control header observed" and FAILED the gate with undefined violation(s) (also a ${violations.length} bug on a number)
  • Fix: status line read before header interpretation; non-2xx = notRun (deployment drift) with observed headers carried in evidence; the count bug fixed
  • Files modified: scripts/lib/cloudflare-probes.mjs
  • Verification: npm run privacy-audit -- --live now exits 2 with PRIVACY AUDIT NOT RUN — deployment drift …; CP2 vector pins the shape
  • Committed in: b959e23/9c55e9a follow-ups (folded into the task-2/3 commits)

Total deviations: 1 auto-fixed (1 bug) Impact on plan: The fix makes the live probe's honesty contract hold end-to-end. The live legs were recorded NOT RUN in docs/PRIVACY-AUDIT.md — honest, exit-2 class, re-run at the launch gate.

Issues Encountered

  • The deployed Worker (araharah-bot.workers.dev) serves a bundle older than the current web/dist (its hashed asset 404s) — correctly reported as deployment drift by the probe; a redeploy (07-04/07-06) plus shipped _headers will turn the live legs green.

Known Stubs

None — every gate/probe is fully wired; no placeholder data flows anywhere.

User Setup Required

None — no external service configuration required. (The --live probes need CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID in the environment; absent credentials degrade to exit 2 by design, never a pass.)

Next Phase Readiness

  • 07-04 (budget-audit) re-runs the live header probe against a deployed build carrying its _headers policy — the two plans share the live-probe surface
  • 07-06 (launch gate) signs docs/PRIVACY-AUDIT.md's signature block as the privacy-audit checklist leg; the --live re-run protocol is documented in the record
  • Baselines intact: bot 125/125, web 165/165; npm run privacy-audit exits 0 (PRIVACY AUDIT CLEAN — 5 gate(s) passed, 0 violations)

Phase: 07-hardening-private-launch Completed: 2026-10-24

Self-Check: PASSED

All 7 created files verified on disk; all 5 task commits verified in git log (87fcb28 test-RED, 430993b, b959e23, 9c55e9a, 8229e36). STATE.md and ROADMAP.md untouched (orchestrator-owned).