diff --git a/TODO.md b/TODO.md index 785f13b..39b1a36 100644 --- a/TODO.md +++ b/TODO.md @@ -18,68 +18,52 @@ Issue branches do NOT touch this file — the manager maintains it on # Status -pre-1.0. No git tags exist. `main` (4f5ecb1) is a working webhook proxy -with auth, CSRF/SSRF protections, login rate limiting, Slack target, -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). +1.0.0 is complete: 55 closed, 0 open. `next` (6874059) is 62 commits +ahead of `main` and a strict fast-forward. No git tags exist yet. -`next` is green — verified both by CI and by cache-defeated container -runs (`docker build --no-cache-filter=lint --no-cache-filter=builder`) — -but the **1.0.0 milestone is no longer complete**. It was reopened on -2026-08-20 by a code-level deployability audit that ran the service end -to end (verdict: -https://git.eeqj.de/sneak/webhooker/issues/33#issuecomment-66686). +The bar was not "the milestone is empty" but "sneak can deploy this and +use it in low-volume production". Every gap the deployability audit +named against that bar is now closed: -The bar for 1.0 is not "the milestone is empty" but "sneak can deploy -this and use it in low-volume production". The audit found the gap -between those two: two instances on one `DATA_DIR` both deliver -(reproduced), a failed listen leaves a live non-serving process that -restart policies never fire on, there is no inbound authentication of -any kind, delivery failures render as a bare word with no status code or -error, a terminally failed delivery can never be replayed, the SSRF -blocklist has no escape hatch so the proxy cannot forward to your own -network at all, and target credentials leak into the per-webhook event -databases. +- `DATA_DIR` locking, so two instances cannot both deliver + (https://git.eeqj.de/sneak/webhooker/issues/201) +- shutdown on listener failure, rather than a live non-serving process + (https://git.eeqj.de/sneak/webhooker/issues/200) +- inbound signature verification + (https://git.eeqj.de/sneak/webhooker/issues/67) +- per-attempt delivery detail in the event log + (https://git.eeqj.de/sneak/webhooker/issues/202) +- replay of a terminally failed delivery + (https://git.eeqj.de/sneak/webhooker/issues/203) +- `ALLOWED_EGRESS_CIDRS`, an allowlist escape hatch for the SSRF guard + (https://git.eeqj.de/sneak/webhooker/issues/204) +- the three credential exposures + (https://git.eeqj.de/sneak/webhooker/issues/205, + https://git.eeqj.de/sneak/webhooker/issues/206, + https://git.eeqj.de/sneak/webhooker/issues/207) -One caveat on reading a green check, narrower than it used to be. A -docs-only commit deliberately replays from the layer cache (#119), so a -green status on such a commit evidences a replay rather than an executed -run; a code commit invalidates the `COPY` layer and genuinely executes. -Superseded runs are no longer the hazard they were: before #152 they -were recorded as `skipped` and rolled up green, and before #119 a warm -layer cache let the gate report success without executing anything, -replaying the previous build's console log so the lie looked like a real -run. Both are fixed. Note: `TODO.md` was deliberately -deleted from this repo in f9a9569 (2026-03-01, #6); its content was -folded into the README TODO section, which this draft reconstructs as -of 2026-07-06. +One caveat on reading a green check: a docs-only commit deliberately +replays from the layer cache +(https://git.eeqj.de/sneak/webhooker/issues/119), so a green status on +such a commit evidences a replay rather than an executed run. A code +commit invalidates the `COPY` layer and genuinely executes. # Next Step -Clear the reopened 1.0.0 milestone. The milestone PR -(https://git.eeqj.de/sneak/webhooker/pulls/111) is held: it carries a -`WIP: ` prefix, no labels and is assigned to `clawbot`, and it stays -that way until the milestone is empty. Correctness first — the -duplicate-delivery lock and the listen-failure shutdown — then the -operability gaps that make the service usable in production, then the -three credential exposures. +Merge the milestone PR (https://git.eeqj.de/sneak/webhooker/pulls/111) +and tag `v1.0.0`. It is `merge-ready` and assigned to sneak; nothing +else gates it. -Three items belong to the owner, none of them blocking. #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. #198 records that `make test` is past the org 20s -target — 46s of test execution inside a 62.8s CI layer — and turns on -which quantity the 60s hard cap governs; it is scoped as the improvement -bug the 20-60s band requires, and should be milestoned instead if the -cap is read as covering the whole invocation. - -After the tag, the largest open cluster is the unmilestoned follow-up -backlog these units generated: #183, #184, #185, #190, #191, #193, #198, -#211 and #212 (encrypting target config at rest, split out of the -credential-leak fix because it needs a key-rotation and re-wrap story). +Post-1.0 follow-ups are open, none blocking the tag: +https://git.eeqj.de/sneak/webhooker/issues/245, +https://git.eeqj.de/sneak/webhooker/issues/246, +https://git.eeqj.de/sneak/webhooker/issues/247 and +https://git.eeqj.de/sneak/webhooker/issues/248. Also still open and +unmilestoned: https://git.eeqj.de/sneak/webhooker/issues/193 (a design +question, not a defect), https://git.eeqj.de/sneak/webhooker/issues/198 +(`make test` is past the org 20s target) and +https://git.eeqj.de/sneak/webhooker/issues/212 (encrypting target config +at rest). # Completed Steps @@ -308,14 +292,16 @@ credential-leak fix because it needs a key-rotation and re-wrap story). # Future Steps -- Manual event redelivery from the web UI — the "Replay" capability the - README describes as planned. No redelivery code exists anywhere in the - tree; events are stored in full, which is all it would be built on -- Delivery status and retry management UI +- Delivery status and retry management UI. Replay of a terminally + failed delivery and per-attempt detail already landed + (https://git.eeqj.de/sneak/webhooker/issues/203, + https://git.eeqj.de/sneak/webhooker/issues/202) - Per-webhook rate limiting in the receiver handler (per-webhook config plus handler enforcement; global limits must not apply to receiver endpoints) -- Webhook signature verification for GitHub and Stripe HMAC formats +- Stripe HMAC signature verification. The GitHub and GitLab schemes + landed with inbound verification + (https://git.eeqj.de/sneak/webhooker/issues/67) - API key authentication for programmatic access (APIKey model exists; Bearer token middleware does not) - REST API v1 @@ -325,9 +311,10 @@ credential-leak fix because it needs a key-rotation and re-wrap story). - OpenAPI specification - Analytics dashboard: success rates, response times, volume - A remember-me option at login -- Password reset flow for a forgotten password. The authenticated - password *change* flow already landed on `main` (#65); reset does not - exist +- Password reset flow for a forgotten password over the web. The + authenticated password *change* flow already landed, and a lost + password is recoverable from the console with `webhooker resetpw` + (https://git.eeqj.de/sneak/webhooker/issues/208) - Later, nice to have - email delivery target type - SNS and S3 delivery targets