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
Check · Review · Land · Live done — Landed and live in production.
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
Sign in to join this conversation.
Pushed. Adds
noncemigration (WITHOUT ROWID, keyed on(app_id, nonce), indexed onseen_at) andsrc/nonce.tswithrememberandsweep.remember(db, appId, nonce, now?)is oneINSERT ... ON CONFLICT DO NOTHING,changes()-checked, so racing posts (app process + terminal) never read-then-write. Malformed input (checked againstsign.ts'sAPP_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_000is asserted> sign.ts'sWINDOW_MS(300s) directly in a test, with the reasoning in a comment: a swept nonce's request is alreadystale, so forgetting it can't reopen a replay window.src/nonce.test.tscovers: 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, tworemembercalls 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 testis green (39 pass).Closes #19