carlos / platform Public

docs: spec §7 and plan record the final-review corrections

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 2713274d9f49bf0d83262197bf4f2dd070b1df52 parent 6b47707
2 files changed, +42 −4
  • docs/superpowers/plans/2026-10-24-edge-protection-1-request-records.md +21 −0
  • docs/superpowers/specs/2026-10-24-edge-request-protection-design.md +21 −4
diff --git a/docs/superpowers/plans/2026-10-24-edge-protection-1-request-records.md b/docs/superpowers/plans/2026-10-24-edge-protection-1-request-records.md
index 55b0dfcab..52ed35dfa 100644
--- a/docs/superpowers/plans/2026-10-24-edge-protection-1-request-records.md
+++ b/docs/superpowers/plans/2026-10-24-edge-protection-1-request-records.md
@@ -10,6 +10,27 @@
**Spec:** `docs/superpowers/specs/2026-10-24-edge-request-protection-design.md` (§7, §8 step 1). Read it before Task 1.
+## Final-review corrections (2026-10-25)
+
+The whole-branch review changed these after the tasks below were built; the
+code follows this list where a task says otherwise:
+- Reference codes are 25 bytes (nonce bytes 12-19, MAC bytes 20-24 over
+ bytes 0-19): 40 characters. `reqrec.CanonicalRef` is the one spelling used
+ by `ParseRef`, the console lookup and the returned code.
+- Blocks and watches have separate caps (`BlockPerMin`, `WatchPerMin`, 300
+ each; `MatchPerMin` is gone); minute records count `dropped_block` and
+ `dropped_watch` instead of `dropped_match`, and add `m_*` method counts.
+- `reqrec.Sink` has a second method, `EdgeSummary`, for minute records: it
+ bypasses the edge rate bucket. In the ring edge records evict only edge
+ records (request records, then blocks, then summaries), never app or
+ platform lines.
+- The stored method is `GET`..`TRACE` or `OTHER`; request text has controls
+ escaped; records carry `env`; QueryNames drops components with no `=`.
+- The edge leaves `Response.When` zero and the recorder stamps it;
+ `newRequestRecorder` takes a `now` clock.
+- The edge-refs answer always carries `record_status`: `found`, `absent` or
+ `unavailable` (a log read error is no longer a 502).
+
## Global Constraints
- Provider-neutral: no AWS SDK or cloud service in any new code (spec §4 anti-goals).
diff --git a/docs/superpowers/specs/2026-10-24-edge-request-protection-design.md b/docs/superpowers/specs/2026-10-24-edge-request-protection-design.md
index 72c714762..32a601414 100644
--- a/docs/superpowers/specs/2026-10-24-edge-request-protection-design.md
+++ b/docs/superpowers/specs/2026-10-24-edge-request-protection-design.md
@@ -486,15 +486,29 @@ backtested.
more base64 or hex characters) are masked, because reset links and signed
URLs carry their secret in exactly that shape.
- What can't be classified is masked, not kept. Calibration loses a little
- evidence; nobody's secret lands in a log.
+ evidence; nobody's secret lands in a log. In practice (final review,
+ 2026-10-25): any run of 24 or more such characters is masked whatever it
+ holds, a standard-base64 token split into path segments at `/` is judged
+ joined, and a query component with no `=` is left out of the names.
+ - Control characters in recorded request text (path, query names,
+ fragment) are stored escaped as `\xNN`, so a record cannot forge a log
+ line or drive the terminal it is printed on. The method is stored as one
+ of `GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`, `CONNECT`,
+ `TRACE` or `OTHER`.
- **Everything else** is counted in memory per app, per minute (status class,
method, probe-catalogue hits, wake caused or not) and flushed in batches. No
storage write per request, ever, and a flood must not turn records into its
own load: under pressure records are sampled, and the counts say how many
- were dropped.
+ were dropped. Per app and minute at most 60 not-found, 300 block and 300
+ watch records are written; blocks and watches are capped separately, so
+ watches cannot use up the blocks' allowance. Minute records carry the
+ status classes, per-method counts (`m_get`, `m_head`, `m_post`, `m_put`,
+ `m_patch`, `m_delete`, `m_options`, `m_other`), wakes, blocks, watches and
+ the dropped counts. Every record carries the route's environment (`env`)
+ when it has one.
- **Shipping** uses the existing per-app log pipeline (`internal/applog`,
`StreamEdge`) and its budgets, so they are readable with `carlos logs` and in
- the console. Retention is the logs family's 7 days (`logshipMaxAge`, `internal/host/logship.go`), changed at plan time from 14 because retention is per family and the calibration report can read two 7-day windows. Edge records have their own rate budget in the sink so they never crowd out the app's own lines.
+ the console. Retention is the logs family's 7 days (`logshipMaxAge`, `internal/host/logship.go`), changed at plan time from 14 because retention is per family and the calibration report can read two 7-day windows. Edge records have their own rate budget in the sink so they never crowd out the app's own lines, and in the sink's buffer they only ever evict each other, never an app or platform line. Minute records bypass the edge rate budget, and the buffer evicts request records first, then blocks, then minute records.
- **Reference codes** are short, unguessable, and resolve (for the app's members
only) to the record: which rule fired, on what, and the one-line exception
that would allow it.
@@ -509,7 +523,10 @@ backtested.
`edgeproof.LoadKeys`) under the label `carlos-edge-ref`. A box without the
key (no deployment store, or a fleet box the store door refuses it to)
writes unsealed codes, which the console decodes and marks unverified. The
- box is carried as a 2-byte hash.
+ box is carried as a 2-byte hash. A code is 25 bytes with an 8-byte random
+ nonce, written as 40 characters of Crockford base32. The console answers
+ from the seal even when the logs cannot be read, and says whether the
+ record was found, is absent, or was unavailable.
- **Calibration needs more:** during the watch window (§8), probe-catalogue and
attack-tier matches also record the upstream response status, so learned
exemptions (§5.3) and promotion decisions have the evidence.