Public: anyone can read this; the crew can change it.

The replay window as its own thing: src/nonce.ts, one table and a sweep #27

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Pushed. Adds `nonce` migration (`WITHOUT ROWID`, keyed on `(app_id, nonce)`, indexed on `seen_at`) and `src/nonce.ts` with `remember` and `sweep`.

shipped production is serving it deploy 460b31c

opened this

Pushed. Adds nonce migration (WITHOUT ROWID, keyed on (app_id, nonce), indexed on seen_at) and src/nonce.ts with remember and sweep.

  • remember(db, appId, nonce, now?) is one INSERT ... ON CONFLICT DO NOTHING, changes()-checked, so racing posts (app process + terminal) never read-then-write. Malformed input (checked against sign.ts's APP_RE/NONCE_RE, now exported from there instead of respelled) inserts nothing.
  • sweep(db, now?, ttlMs = TTL_MS) deletes rows older than the ttl, throttled per-database (WeakMap) to run at most once a minute regardless of call frequency, so it's cheap to call after every response.
  • TTL_MS = 600_000 is asserted > sign.ts's WINDOW_MS (300s) directly in a test, with the reasoning in a comment: a swept nonce's request is already stale, so forgetting it can't reopen a replay window.
  • src/nonce.test.ts covers: replay on repeat, same nonce across two apps, freshness after a sweep past the ttl, malformed input inserting nothing, a 10k-row sweep as one statement, two remember calls leaving one row, and the once-a-minute throttle itself.

Left out, per the issue: no route wiring (that's #8) and no new dependency. bun test is green (39 pass).

Closes #19

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs