Repository navigation
fix(gateway): Check key spend limits before a video starts - #1267
Conversation
Video creates and status polls no longer keep a hold on the key's caps once answered; only the retrieve that delivers the file is charged. The gateway passes the room left under the key's caps to the buyer, which refuses a video priced above it before any job starts.
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
|
@reviewant re-review Addressed the P1 in 9f09274: an accepted video's price now stays reserved against the key/member/workspace caps until delivery (moved onto the delivery's hold until its charge settles), job failure, or 24 h. The buyer reports the price on the accepted create ( |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
|
@reviewant re-review Both P1s addressed in the latest commit:
|
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
|
@reviewant re-review Addressed the P1: a poll error no longer releases a job's commitment by HTTP status. It is released only for an authoritative terminal answer: a 200 status with |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
|
@reviewant re-review Addressed both:
|
|
@kotevcode the review run failed (RuntimeError); see the agent logs. |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
|
@reviewant re-review Addressed the P2: the commitment lifetime now matches the buyer's job lifetime (30 days, same as the resource-route TTL and |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
|
@reviewant re-review Addressed the P2 by making a failed status authoritative in the buyer: when a retrieve's normalized answer is |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
…ore answering failed
|
@reviewant re-review Addressed both P2s in the buyer proxy:
|
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
…eves to the creating key
…exactly their price
|
@reviewant re-review Since round 8 the video lifecycle was redesigned after an internal adversarial audit (see the updated PR description): commitments are released only on buyer-authoritative signals (the buyer's charge, lowered by each charge's amount; the buyer-only |
|
@reviewant re-review Head is now 2df69b7: in addition to the previous comment, a job that still holds its price is always admitted for its own polls and download (hold 0), even when unrelated spend has filled the caps since — its commitment already covers its delivery charge — so a committed job can always finish or be retired. |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
…e stored; free videos hold nothing
|
@reviewant re-review Head ae5c47e addresses round 9: (P1) commitment storage now fails closed — a failed save is kept and retried, a failed startup load is retried, and until both succeed requests of capped keys answer |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
|
@reviewant re-review |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
…o a restart cannot lose an accepted job
|
@reviewant re-review |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
…pricing and old buyers
|
@reviewant re-review |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
|
@reviewant re-review |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
|
@reviewant re-review |
|
Superseded by a newer review: #1267 (comment) (previously reviewed |
…t be saved, and still pay the seller
|
@reviewant re-review |
Antseed Review SummaryThe PR adds video-price prechecks against gateway key, member, and workspace spending limits, then persists accepted-job commitments until the corresponding spend arrives or the job is retired. It also adds buyer capability detection, ownership checks, serialized video retrieval, and delivery-state handling so failed persistence or interrupted downloads do not prematurely release reserved budgets. Configuration, documentation, and regression tests accompany these changes.
Confidence Score: 5/5No verified merge-blocking issues found in the reviewed paths. The buyer-core suite passed all 45 tests; targeted node payment-manager and video-reserve-flow tests passed all 97 tests; the full CLI suite passed 1,338 tests with one skipped and no failures. Reviewed paths included budget admission and commitment recovery, caller-provided video caps, buyer-only response headers, serialized retirement, and delivery persistence. Review coverage is incomplete: not every changed test or surrounding core caller was read in full, and live seller interoperability and crash timing were not independently exercised beyond the existing tests; this is not a claim of exhaustive verification. Files that need attention:
|
| Filename | Overview |
|---|---|
apps/cli/src/gateway/accounting.ts |
Adds capped video admission, persistent commitments, recovery, and spend reconciliation; no concrete defect was established in the reviewed accounting paths. |
apps/cli/src/gateway/server.ts |
Enforces buyer capability and job ownership, forwards video allowances, and records acceptance and delivery outcomes; reviewed regions revealed no verified issue. |
apps/cli/src/gateway/store.ts |
Adds transactional commitment storage and request-history queries used for recovery and ownership. |
apps/cli/src/gateway/store-migrations.ts |
Appends a video-commitments table migration without changing the existing SQL migration entries. |
apps/cli/src/proxy/buyer-proxy.ts |
Passes local video caps to the buyer and serializes retrieval and durable retirement of each job. |
apps/cli/src/proxy/video-download.ts |
Distinguishes delivered, unrecorded, failed, and unavailable jobs while terminating streams the buyer did not accept. |
packages/buyer-core/src/buyer-payment-negotiator.ts |
Checks estimated video cost against the caller's remaining allowance before initiating the paid request. |
packages/buyer-core/src/buyer-request-handler.ts |
Strips seller-controlled copies of buyer-only headers and reports buyer-calculated prices and recorded delivery outcomes. |
packages/buyer-core/src/buyer-payment-manager.ts |
Tightens video authorization bounds and tracks delivery persistence failures; targeted payment tests passed. |
CHANGELOG.md |
Documents the user-facing budget enforcement, compatibility behavior, persistence, and download changes. |
Diagram
sequenceDiagram
participant Client
participant Gateway
participant Store
participant Buyer
participant Seller
Client->>Gateway: Create video
Gateway->>Store: Persist budget reservation
Gateway->>Buyer: Create with remaining allowance
Buyer->>Buyer: Validate advertised video price
Buyer->>Seller: Submit accepted request
Seller-->>Buyer: Job identifier
Buyer-->>Gateway: Job identifier and buyer calculated price
Gateway->>Store: Replace reservation with commitment
Gateway-->>Client: Accepted job
Client->>Gateway: Retrieve owned job
Gateway->>Buyer: Retrieve video
Buyer->>Buyer: Serialize retrieval for this job
Buyer->>Seller: Request status or video
alt Accepted delivery
Seller-->>Buyer: Video stream
Buyer->>Buyer: Record delivery and persistence outcome
Buyer-->>Gateway: Complete accepted stream
Buyer-->>Gateway: Attributed spend
Gateway->>Store: Record spend and reduce commitment
else Terminal failure
Seller-->>Buyer: Failed or missing job
Buyer->>Buyer: Persist route retirement
Buyer-->>Gateway: Retired job marker
Gateway->>Store: Remove commitment
end
Core files read in full: apps/cli/src/gateway/accounting.ts, apps/cli/src/gateway/video-jobs.ts, apps/cli/src/proxy/resource-routes.ts, apps/cli/src/proxy/video-download.ts
Reviews (18): Last reviewed commit: fix(gateway): Check key spend limits before a video starts (648efe72f3) | requested by @kotevcode | Tag @reviewant with "re-review" to run again
Description
Video requests through the gateway could fail with
402 spend_limit_reachedon a key that had spent nothing: every video create and status poll held the per-request reserve ($0.30) for 60 s after it was answered, although only the delivery is charged. Fixing that properly means the gateway has to account for a video job over its whole life, so this PR makes video jobs count against key/member/workspace caps exactly once, from start to charge, and closes the edge cases around it (found by #1201 smoke testing, review rounds, and an adversarial audit).Gateway (
apps/cli/src/gateway)video-budgeton its spend feed (read fresh per create); an older buyer would ignore the allowance, so such creates answer503 buyer_upgrade_required. Uncapped keys are unaffected. Capped creates are not held for a restarting buyer (it may come back older).429 video_pricing_in_progressinstead of402.x-antseed-max-video-usdc) and holds that room (≤payments.maxVideoRequestUsdc, default $5) until priced, so overlapping creates cannot share it.x-antseed-video-price-usdc, strictly parsed, clamped to the allowance; whole allowance if missing/invalid) in a persistedvideo_commitmentstable (migration 10). A capped create stores a conservative reservation before it is sent (503 if it cannot), swapped for the job's commitment in one transaction; at startup a leftover reservation becomes its job's commitment when the request log names the job, and is dropped otherwise (no key could retrieve that job). The job ID is parsed from the buyer's answer before, and independently of, request-log writes. Polls and downloads of the job are admitted without counting it twice and hold nothing after they answer.x-antseed-video-job-retiredmarker, 1 h after a download the buyer accepted (x-antseed-video-delivered) with no charge, or 30 days. Revoked keys' jobs stop counting once no admitted retrieve or pending delivery charge can charge them; uncapped keys record the price so later caps count it. Nothing a seller sends can release or inflate it.Buyer (
buyer-core,apps/cli/src/proxy)unrecorded(job kept, not retired) until the clear is saved (the job is then no longer chargeable), using the job's own billing facts rather than the evictable request cache.unknown.Release Notes
Types of Changes
Checklist