Files
hosted-gateway/package.json
Claude 5b2888aade fix: reservation leak, spoofable delivery envelopes, and Decimal wei rendering
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
2026-08-07 07:28:09 +00:00

49 lines
1.6 KiB
JSON

{
"name": "hosted-gateway",
"version": "0.1.0",
"private": true,
"description": "Hosted multi-tenant frontend for the Token4Token inference network. One operator wallet; xBZZ-denominated off-chain ledger.",
"scripts": {
"dev:api": "bun run --watch src/api.ts",
"dev:worker": "bun run --watch src/worker.ts",
"start:api": "bun run src/api.ts",
"start:worker": "bun run src/worker.ts",
"db:generate": "bunx prisma generate",
"db:push": "bunx prisma db push",
"db:migrate": "bunx prisma migrate deploy",
"db:seed": "bun run prisma/seed/index.ts",
"test:e2e": "bun test --preload ./test/setup.ts ./test/**/*.e2e-spec.ts",
"test:unit": "bun test ./test/unit",
"typecheck": "bunx tsc --noEmit"
},
"//comment": "Prisma 7 reads its config from prisma.config.ts at the package root, not from package.json#prisma.",
"dependencies": {
"@elysiajs/bearer": "^1.2.0",
"@elysiajs/cors": "^1.2.0",
"@elysiajs/swagger": "^1.2.0",
"@ethersphere/bee-js": "^11.0.0",
"@noble/ciphers": "^1.3.0",
"@noble/curves": "^1.9.0",
"@noble/hashes": "^1.8.0",
"@prisma/adapter-pg": "^7.0.0",
"@prisma/client": "^7.0.0",
"argon2": "^0.41.1",
"bullmq": "^5.34.0",
"elysia": "^1.2.25",
"google-auth-library": "^9.15.0",
"ioredis": "^5.6.0",
"jsonwebtoken": "^9.0.2",
"pino": "^9.5.0",
"pino-pretty": "^11.2.2",
"viem": "^2.21.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/jsonwebtoken": "^9.0.9",
"bun-types": "latest",
"prisma": "^7.0.0",
"typescript": "^5.7"
},
"//prisma-config": "Prisma 7 — see prisma.config.ts"
}