Seven bugs found by auditing the billing and PSS paths.
1. Reservations were never released on ack/delivery timeout. Both failure
paths in gateway/context.ts marked the GatewayJob terminal *before* calling
releaseReservation, and releaseReservation bailed out on exactly those
statuses — so every failed job permanently inflated the tenant's
reservedXbzzWei and shrank their available balance. Replace the status check
with GatewayJob.reservationReleasedAt as an explicit compare-and-set marker,
which makes the release exactly-once regardless of ordering, and give
settleXbzz the same guard so a job that was released locally and claimed
on-chain anyway can't decrement the reservation twice. The migration returns
the xBZZ already stuck on existing accounts.
2. job_ack / job_deliver were only checked for a valid signature, never for
*whose* signature. Anyone who learned a routing id could sign their own
job_deliver, pointing at a chunk encrypted to the gateway's (published) PSS
key, and hand the tenant an arbitrary completion. Drop any envelope whose
`from` isn't the provider the job was posted to.
3. BigInt(decimal.toString()) throws on any wei value >= 1e21 (1000 xBZZ),
because decimal.js switches to exponential notation there. That took out
GET /accounts/me and, worse, wedged the claim watcher's settle loop. Route
every Decimal→string conversion through toFixed().
4. A cancelJob failure at the ACK deadline left the job with no timer at all,
so the chat request hung forever holding a semaphore slot and a reservation.
Fall through to the delivery deadline instead.
5. `?limit=abc` reached Prisma as `take: NaN` (500). Clamp and fall back.
6. A fractional max_tokens made BigInt() throw; a negative one budgeted a
negative reservation. Validate at the schema and defend in the estimator.
7. model.<entity>.delete() announced the deletion on the queue before
performing it, so a failed delete still told consumers the row was gone.
Verified against a real Postgres: the reservation lifecycle (release-after-
terminal, settle-after-release, concurrent release/settle, idempotent settle)
and the migration's repair of leaked reservations on pre-fix data.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mfkh741kNCZeBdk4xyM9qR