From d2cebb578380fb3d2f997fdaa2bcd1841779a37b Mon Sep 17 00:00:00 2001 From: clawbot Date: Tue, 18 Aug 2026 02:06:43 +0000 Subject: [PATCH] Correct TODO.md milestone state and record seventeen landed units The Status section claimed next held the completed 1.0.0 milestone with every issue closed. Four are open (#176, #178, #186, #187), so a merge of next to main would have shipped that claim to main. Next Step still named #115 and #125 as open owner decisions; both landed. It now names the real open items, #150 and #112, and the forced merge order for the remaining four. Completed Steps was seventeen units behind, back to 2026-08-12. --- TODO.md | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 7 deletions(-) diff --git a/TODO.md b/TODO.md index 2ea43e1..acbc71f 100644 --- a/TODO.md +++ b/TODO.md @@ -24,8 +24,9 @@ event retention (#63), the database archiving target (#43), the admin password change flow (#65), policy compliance (#6), pinned lint tooling (#55), and fail-loud configuration parsing (#80). -`next` holds the completed 1.0.0 milestone: every issue in it is closed, -and it is verified green by cache-defeated container runs +`next` holds the 1.0.0 milestone less its final four issues (#176, #178, +#186, #187 — all in review or held on merge order), and is verified +green by cache-defeated container runs (`docker build --no-cache-filter=lint --no-cache-filter=builder`). The CI status is not independently claimed here: a superseded run is recorded as `skipped` and still rolls up green, so a commit status on @@ -39,15 +40,78 @@ of 2026-07-06. # Next Step -Merge the milestone PR to `main` and tag 1.0.0 from it. +Land the last four 1.0.0 issues, then merge the milestone PR to `main` +and tag 1.0.0 from it. Merge order is forced by a real conflict on +`README.md` and `internal/middleware/middleware.go`: #186, then #176, +then #178, then #187. -Two decisions are open and belong to the owner, neither blocking the -tag: #115 (mask the `http` target's destination URL, implemented -speculatively and awaiting a yes or no) and #125 (whether IPv6 -rate-limit keys should bucket by `/64`). +Two items belong to the owner, neither blocking the tag. #150 was +decided by the manager rather than left to stall the queue and is +flagged on the issue for reversal if that call was wrong. #112 (whether +`Completed Steps` should exist at all, given it once conflicted on every +unit) is unanswered; the provisional ruling in force is that issue +branches do not touch this file. # Completed Steps +- 2026-08-18 Send the chi route pattern to Sentry rather than the + concrete path. The receiver's path carries the entrypoint capability + token, so every Sentry event from `/webhook/{uuid}` shipped a live + credential to a third party. Request `Data`, `QueryString`, `Cookies` + and `Env` are dropped and headers reduced to an allowlist (#179) +- 2026-08-18 Read form fields from the POST body only. `r.FormValue` + merges the query string, so a login could be driven by URL parameters + — putting the password somewhere that lands in access logs, proxy + logs and browser history (#160) +- 2026-08-18 Verify login credentials before spending rate-limit + budget, so a flood of wrong passwords cannot lock out the account it + is guessing at. The manager took this decision rather than stall the + queue; it is flagged on the issue for reversal (#150) +- 2026-08-18 Run all linting in Docker via `Dockerfile.lint`. Host lint + was wrong in both directions from version skew and shared caches. + `script/lint` asserts the summary line, because `--no-cache-filter` + silently ignores a stage name it does not match — the flag that makes + the gate meaningful fails open (#109) +- 2026-08-18 Serve an event's full stored body over HTTP. The list + query truncates for rendering, and that truncated value was the only + way to read a body, so the full payload was unreachable (#157) +- 2026-08-18 Bound the access log line against client-chosen text. + `internal/logfield` budgets by *encoded* bytes, not runes, so a + handler's JSON escaping cannot multiply a field past its allowance + (#146) +- 2026-08-18 Mark superseded CI commits `failure` rather than + `skipped`. A skipped run rolls up green, so a commit that was never + tested reported success (#152) +- 2026-08-18 Set `fx.StopTimeout` inside the container stop grace, so + shutdown hooks are bounded by a deadline the orchestrator will + actually honour rather than being killed mid-flush (#134) +- 2026-08-17 Bucket IPv6 rate-limit keys by `/64`. A single allocation + hands out 2^64 addresses, so per-address keying let one client mint + unlimited buckets. Manager decision, recorded on the issue (#125) +- 2026-08-17 Correct release-blocking README and startup-warning + inaccuracies, including claims about behaviour the code does not have + (#151) +- 2026-08-17 Fetch and verify Alpine.js at build time against + `static/vendor.sha256` instead of committing the minified blob, so + the dependency is pinned by hash rather than by trust (#145) +- 2026-08-17 Bound the event log's rendered bodies in the query itself, + so a large stored payload cannot be read into memory just to be + truncated for display (#135) +- 2026-08-17 Mask the `http` target's destination URL in the UI: it can + carry a bearer credential in its path or query, and was rendered + verbatim. Manager decision to mask unconditionally (#115) +- 2026-08-14 Bound shutdown hooks by their stop context, so a hook that + hangs cannot hold the process past its grace period (#102) +- 2026-08-14 Render templates via a buffer rather than the + `ResponseWriter`, so a template error part-way through cannot commit + a 200 and then fail — the response is written only once it is whole + (#123) +- 2026-08-14 Align the session codec's max-age with the 7-day absolute + cap. The codec accepted cookies the session layer considered expired, + so the cap was enforced in one place and not the other (#108) +- 2026-08-12 Warn when `TRUSTED_PROXIES` is empty in production, where + the safe default silently discards forwarded headers and every client + rate-limits as the proxy's address (#149) - 2026-08-12 Bound the receiver rate limit per client IP across the whole `/webhook/*` route. The existing limiter keyed on the request path and `/webhook/{uuid}` matches any single segment, so a client