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
49 lines
1.6 KiB
JSON
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"
|
|
}
|