Files
webhooker/TODO.md
clawbot 20a050b49d
All checks were successful
check / check (push) Successful in 3m3s
Root background loops at context.Background() (closes #97)
The context fx hands an OnStart hook is derived with
context.WithTimeout(ctx, StartTimeout) — 15 seconds by default — and
is cancelled once the start phase ends. It is a start-phase context,
not an application-lifetime one. Two components derived their
long-lived loops from it and so stopped running roughly fifteen
seconds after boot.

Engine.start rooted the entire worker pool, restart recovery, and the
retry sweep in it. Every worker returned on ctx.Done() shortly after
startup, so the process kept receiving and persisting inbound events
while nothing at all forwarded them: deliveryCh filled up and started
logging "delivery channel full" with no consumer left. That is the
whole purpose of the application.

RetentionReaper.start had the same defect. With the default one-hour
RETENTION_SWEEP_INTERVAL the loop was cancelled forty-five minutes
before its first tick, so the reaper never ran a single sweep and
per-webhook event databases grew without bound.

Both now derive their loop context from context.Background(). Their
lifetime is bounded by OnStop, which already cancels and waits on the
WaitGroup, so shutdown is unchanged. Each hook registration moves into
a registerHooks method, the OnStart parameter is named _ so the trap
cannot be reintroduced by silencing an unused-parameter warning, and a
comment at each start explains why the hook context must not be used.
This matches the shape of the same fix applied to the archive sweeper.

The new lifecycle tests drive the genuine registered hooks with an
already-cancelled OnStart context and assert the loops still do work
afterwards — a task delivered, an expired event reaped. Reverting
either fix makes its pair of tests fail. Each component also gets a
shutdown test asserting OnStop cancels the loop and wg.Wait() returns
inside a bounded timeout, so the fix does not trade a startup bug for
a shutdown hang.

iWaitForStatus becomes iWaitForDelivered: every call site waits for
the delivered status, and the two added call sites pushed it past
unparam's threshold for reporting an always-identical argument.
2026-08-09 05:15:13 +00:00

4.0 KiB

Workflow

  • branch (from main)
  • do the work in Next Step
  • move Next Step to the top of Completed Steps
  • move the top item of Future Steps into Next Step
  • commit (TODO.md changes in the same commit as the work)
  • merge to main if the branch is not protected, otherwise open a PR
  • push

Status

pre-1.0. No git tags exist. main (afe88c6) is a working webhook proxy with auth, CSRF/SSRF protections, login rate limiting, Slack target, policy compliance (#6), and pinned lint tooling (#55). 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.

Next Step

Implement automatic event retention cleanup based on retention_days: a periodic maintenance job that deletes Events, Deliveries, and DeliveryResults older than the parent webhook's retention_days from each per-webhook event database. The field exists on the Webhook model and the README promises the behavior, but nothing enforces it, so event databases currently grow without bound.

Completed Steps

  • 2026-08-09 Root the delivery engine's worker pool and the retention reaper's sweep loop at context.Background() rather than the fx OnStart hook context (#97), which carries fx's 15s start timeout and killed both roughly fifteen seconds after boot: the proxy silently stopped delivering webhooks entirely, and the reaper never ran a single sweep under its default one-hour interval
  • 2026-08-07 Update golangci-lint to v2.12.2 (Docker image digest in Dockerfile, release-archive sha256 pins in script/bootstrap), adopt the canonical .golangci.yml (v2 linters.settings layout so lll/funlen/cyclop/dupl thresholds actually apply), and fix all newly surfaced lint findings
  • 2026-07-07 Adopted scripts-to-rule-them-all: script/ entrypoints, Makefile shims, README Entrypoints section
  • 2026-03-25 pin golangci-lint Docker image for linting (#55)
  • 2026-03-18 CSRF middleware detects TLS per-request, fixing login over plain HTTP and behind reverse proxies (#54)
  • 2026-03-17 root path redirects based on auth state (#52)
  • 2026-03-17 CSRF protection, SSRF prevention for HTTP delivery targets with DNS rebinding defense, and per-IP login rate limiting (#42)
  • 2026-03-17 Slack target type for incoming webhook notifications (#47)
  • 2026-03-17 Dockerfile absolute paths and static linking (#49); absolute dev DATA_DIR default and clarified env docs (#46)
  • 2026-03-05 security headers middleware, session regeneration on login, request body size limits (#41)
  • 2026-03-04 tests for delivery, middleware, and session packages (#32); removed globals.Buildarch (#31)
  • 2026-03-04 1.0 MVP merge: Webhook/Entrypoint/Target rename, core delivery engine with bounded worker pool and circuit breaker, parallel fan-out, per-webhook event databases, management UI (#16)
  • 2026-03-01 repo brought to REPO_POLICIES standards; TODO.md folded into README (#6)

Future Steps

  • Manual event redelivery from the web UI (replay is a core promised capability in the README rationale)
  • Delivery status and retry management UI
  • 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
  • API key authentication for programmatic access (APIKey model exists; Bearer token middleware does not)
  • REST API v1
    • CRUD for webhooks, entrypoints, targets
    • event viewing and filtering endpoints
    • event redelivery endpoint
    • OpenAPI specification
  • Analytics dashboard: success rates, response times, volume
  • Session expiration tuning and a remember-me option
  • Password change and reset flow
  • Later, nice to have
    • email delivery target type
    • SNS and S3 delivery targets
    • data transformations (e.g. webhook to Slack message formatting)
    • JSONL file delivery with periodic S3 upload
    • webhook event search and filtering
    • multi-user with role-based access control