Download Latest Version v0.12.267 -- a lost response on the pre-auth path could sign a second payment source code.zip (56.0 MB)
Email in envelope

Get an email when there's a new version of ClawRouter

Home / v0.12.263
Name Modified Size InfoDownloads / Week
Parent folder
README.md 2026-09-01 1.9 kB
v0.12.263 -- a clock race that let concurrent payments both clear the same cap source code.tar.gz 2026-09-01 55.3 MB
v0.12.263 -- a clock race that let concurrent payments both clear the same cap source code.zip 2026-09-01 55.4 MB
Totals: 3 Items   110.7 MB 0

Fixed — a live-clock race let in-flight reservations vanish from the spend caps

SpendControl.getSpendingInWindow() read the clock a second time and compared it against a window bound the caller had already built from a first read. On Date.now(), a millisecond ticking between the two makes the guard go false and silently drops the whole pending total from the figure the hourly/daily cap is checked against.

Reservations are the concurrency-safety mechanism — the x402 pre-sign hook does check-then-reserve() synchronously precisely so two concurrent payments cannot both clear the same remaining budget. With the pending total missing, they can: a $5 hold disappears and a second $5 payment sees the full $5 of an already-exhausted $5 cap. The caller's single now is threaded through instead.

Every existing test injected a frozen clock, so the two reads always matched and the sub-millisecond window went uncaught. There is now a live-clock test.

Fixed — ?days= on /stats silently zeroed or mislabeled the report

?days=abc became NaN, survived Math.min, and turned slice(0, NaN) into slice(0, 0) — the endpoint reported zero usage. ?days=-1 survived Math.min(-1, 30) and slice(0, -1) dropped the newest day while the response labelled itself "last -1 days". One resolveStatsDays() guard covers non-numeric, NaN, zero and negative.

Fixed — logs --days -1 dropped the newest day instead of showing more history

The sibling on the path that does not go over HTTP. parseInt(raw, 10) || 1 lets a negative through (truthy), and log files are newest-first, so a negative slice trimmed from the end. Guarded at the sink in formatRecentLogs so every caller is covered.


Thanks to @erhnysr for [#286] and [#285].

809 tests green, typecheck and prettier clean, dist smoke check passed. Published to npm with SLSA provenance.

Source: README.md, updated 2026-09-01