Files
webhooker/TODO.md
sneak 0c1be884fd
All checks were successful
check / check (push) Successful in 2m40s
Add inactivity-based session timeout (closes #66)
Sessions had only a 7-day absolute lifetime, and that cap was enforced
only by the cookie's MaxAge -- i.e. only by the browser. An abandoned
session stayed usable for the full week.

Sessions are now bounded by two independent, server-enforced clocks,
and end at whichever expires first:

- absolute: created_at + 7 days, stamped once by SetUser and never
  rewritten, so no amount of activity can extend it
- idle: last_seen + SESSION_IDLE_TIMEOUT (default 24h), pushed forward
  by the new Session.Touch

Both deadlines are checked in Session.expired, which IsAuthenticated
now consults, so every existing authentication decision honours them
without each call site having to remember.

Activity means a request that passes RequireAuth, which is the only
place Touch is called; an unauthenticated request carrying the cookie
cannot keep a session alive. Touch re-checks authentication itself so
that guarantee does not depend on the call site.

To avoid re-issuing the session cookie on every authenticated request,
Touch rewrites last_seen only once it is older than a tenth of the idle
window. The session therefore expires up to 10% early relative to the
user's true last request, never late.

An authenticated session carrying no timestamps (a cookie minted before
this change) is treated as expired, so the failure mode of the upgrade
is one forced re-login rather than an unbounded session.

Tests use an injected clock rather than sleeps and cover idle expiry,
refresh on activity, an actively used session still dying at the
absolute cap, refusal to refresh unauthenticated or expired sessions,
disabled idle expiry, and startup aborting on an unparseable
SESSION_IDLE_TIMEOUT.
2026-08-10 14:07:49 +00:00

5.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 (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). 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

Manual event redelivery from the web UI (replay is a core promised capability in the README rationale).

Completed Steps

  • 2026-08-09 Inactivity-based session timeout: sliding idle expiry (SESSION_IDLE_TIMEOUT, default 24h) refreshed on authenticated requests, with the 7-day absolute cap kept as an independent backstop that activity never extends (#66)
  • 2026-08-09 Restart recovery and the 60s retry sweep terminally fail an orphaned retrying delivery whose target type no longer supports retries, recording a DeliveryResult with the reason instead of leaving the delivery stuck forever (#82)
  • 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-09 Archive writer lifecycle (#89): deleting a webhook (or its last database target) evicts the cached archive writer and closes its handle while deliberately leaving archive-{webhookID}.db on disk, and a new ArchiveSweeper prunes idle archives on the existing RETENTION_SWEEP_INTERVAL without ever creating an archive file
  • 2026-08-09 Configuration parsing fails loudly on set-but-unparseable environment values: envInt removed in favour of envPositiveInt plus a PORT range check, envBool now parses with strconv.ParseBool, and defaults apply only to unset variables (#80)
  • 2026-08-07 Automatic event retention cleanup based on retention_days, deleting expired events, deliveries, and delivery results from each per-webhook event database (#63)
  • 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

  • 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
  • A remember-me option at login
  • 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