Put a Budget Boundary Between Agent Beacon and Your Model Bill

It's 3:17 AM. Your phone buzzes. The AWS billing alarm just fired: your team's AI spend crossed $2,000 in six hours. You ssh into the gateway, tail the logs, and find it: a junior engineer's agent harness hit a context window ceiling, retried with truncated history, generated garbage, stored that garbage in memory, then retrieved it on the next attempt. The loop ran 847 times before anyone noticed.

Persistent agent memory can make a failed task expensive twice: first when the agent generates a bad result, then when subsequent attempts retrieve that history and repeat the work.

For a ten-engineer team evaluating Asymptote-Labs/agent-beacon, the immediate problem is not memory quality alone. It is preventing one developer's retry loop from consuming everyone's monthly AI budget.

The repository's supplied description positions it as a cross-harness, self-improving memory layer. That is the scope of this evaluation; I have not verified its current implementation or configuration interfaces. The integration below therefore sits outside Beacon rather than assuming it provides billing controls.

Memory Is Not the Billing Authority

Keep memory, execution, and spending authority separate:

Beacon-enabled harness → seat-authenticated gateway → model relay

Beacon supplies memory to whichever harness adopts it. The harness sends model requests through the gateway using an individual sub-token. Only the gateway holds the upstream credential.

For this reference topology, the relay is https://b-lost.com/v1. Its compatibility, model limits, pricing, and retention terms must be verified before deployment; this article does not report measured benchmark results.

The gateway owns monthly token and dollar ceilings. It must reserve capacity before forwarding a request. Charging only after completion lets concurrent requests overspend the remaining balance.

The implementation below conservatively reserves an entire model context window per request, then releases the unused portion after receiving usage. This requires an upstream-enforced maximum covering input plus output, including any billable reasoning tokens. Without that contract, the reservation is not a reliable upper bound.

One Gateway, One Durable Ledger

Install fastapi, uvicorn, and httpx. Save this as gateway.py. Set UPSTREAM_KEY, MODEL, CONTEXT_TOKENS, MICROUSD_PER_TOKEN, and SEATS_JSON.

For example, SEATS_JSON='{"random-seat-secret":"alice"}' assigns Alice a credential. The defaults grant each seat one million tokens and $100 monthly. MICROUSD_PER_TOKEN=10 represents a conservative $10-per-million-token accounting rate, not a quoted relay price.

import datetime as dt import json import os import sqlite3 from contextlib import contextmanager import httpx from fastapi import FastAPI, HTTPException, Request app = FastAPI() BASE_URL = "https://b-lost.com/v1" SEATS = json.loads(os.environ["SEATS_JSON"]) WINDOW = int(os.environ["CONTEXT_TOKENS"]) RATE = int(os.environ["MICROUSD_PER_TOKEN"]) TOKEN_CAP = int(os.getenv("TOKEN_CAP", "1000000")) MONEY_CAP = int(os.getenv("MONEY_CAP_MICROUSD", "100000000")) assert WINDOW > 0 and RATE > 0 @contextmanager def ledger(): c = sqlite3.connect("budget.sqlite", timeout=10) try: c.execute("BEGIN IMMEDIATE") yield c c.commit() except BaseException: c.rollback() raise finally: c.close() with ledger() as c: c.execute("""CREATE TABLE IF NOT EXISTS seats ( month TEXT, seat TEXT, used INTEGER DEFAULT 0, held INTEGER DEFAULT 0, PRIMARY KEY(month, seat))""") c.execute("""CREATE TABLE IF NOT EXISTS daily ( day TEXT PRIMARY KEY, cost INTEGER DEFAULT 0)""") @app.post("/v1/chat/completions") async def complete(request: Request): credential = request.headers.get("authorization", "") seat = SEATS.get(credential.removeprefix("Bearer ")) if not credential.startswith("Bearer ") or seat is None: raise HTTPException(401, "Invalid seat credential") # Enforce the transport body limit at the ingress, too. raw = await request.body() if len(raw) > 64000: raise HTTPException(413, "Request too large") try: body = json.loads(raw) messages = body["messages"] if not isinstance(messages, list): raise ValueError() except (ValueError, KeyError, TypeError): raise HTTPException(400, "Expected a messages array") now = dt.datetime.now(dt.timezone.utc) month, day = now.strftime("%Y-%m"), now.date().isoformat() with ledger() as c: c.execute("INSERT OR IGNORE INTO seats(month,seat) VALUES (?,?)", (month, seat)) used, held = c.execute( "SELECT used,held FROM seats WHERE month=? AND seat=?", (month, seat)).fetchone() projected = used + held + WINDOW if projected > TOKEN_CAP or projected * RATE > MONEY_CAP: raise HTTPException(429, "Monthly budget unavailable") c.execute("UPDATE seats SET held=held+? WHERE month=? AND seat=?", (WINDOW, month, seat)) # No automatic retries: an ambiguous failure retains its reservation. try: async with httpx.AsyncClient(timeout=90) as client: response = await client.post( f"{BASE_URL}/chat/completions", headers={"Authorization": f"Bearer {os.environ['UPSTREAM_KEY']}"}, json={"model": os.environ["MODEL"], "messages": messages, "max_tokens": min(1024, WINDOW), "stream": False}) response.raise_for_status() result = response.json() tokens = result["usage"]["total_tokens"] if type(tokens) is not int or not 0 <= tokens <= WINDOW: raise ValueError("Usage violates accounting contract") except Exception: raise HTTPException(502, "Upstream uncertain; reservation retained") with ledger() as c: c.execute("""UPDATE seats SET held=held-?, used=used+? WHERE month=? AND seat=?""", (WINDOW, tokens, month, seat)) c.execute("INSERT OR IGNORE INTO daily(day) VALUES (?)", (day,)) before = c.execute("SELECT cost FROM daily WHERE day=?", (day,)).fetchone()[0] cost = tokens * RATE c.execute("UPDATE daily SET cost=cost+? WHERE day=?", (cost, day)) if before < 50000000 <= before + cost and os.getenv("ALERT_URL"): try: async with httpx.AsyncClient(timeout=5) as client: await client.post(os.environ["ALERT_URL"], json={ "text": f"Daily AI accounting crossed $50 on {day}"}) except httpx.HTTPError: pass # Use a durable outbox if alert delivery must be guaranteed. return result 

Run uvicorn gateway:app --host 127.0.0.1 --port 8080 --no-access-log. Configure the harness's OpenAI-compatible client with that local /v1 URL and its seat credential. Beacon's memory traffic must remain separately governed.

Where Production Differs From the Tutorial

SQLite's BEGIN IMMEDIATE serializes admission across workers sharing the same local database. Put that file on persistent local storage. Multiple replicas with independent files would each grant the full budget; this design deliberately targets one small-team gateway.

A timeout is not evidence that generation stopped. Retaining the reservation prevents optimistic refunds after an upstream request may already have incurred charges. A process crash has the same outcome. Reconcile held balances against provider records before releasing them; this compact implementation lacks the per-request ledger needed for automated reconciliation.

Accounting uses the highest applicable token rate for every reported token. That sacrifices precision for understandable limits. Flat request fees, tool charges, or unreported billable tokens invalidate that model and require additional reservations.

The webhook detects the $50 threshold crossing, but it is best-effort. A crash between database commit and delivery can lose the alert. Billing protection must not depend on notification delivery.

The Hard Edge Cases

The 64KB body limit is arbitrary. A request containing Base64-encoded images or large few-shot demonstrations can legitimately exceed it. The limit exists because FastAPI buffers the entire request body into memory before your handler runs. Without it, a malicious client can exhaust gateway RAM. The correct mitigation is streaming validation against a schema, but that requires custom middleware.

Held reservations accumulate when the gateway crashes between making an upstream call and recording usage. A nightly reconciliation job could compare SUM(held) against zero usage reports from the relay, but that requires the relay to expose per-credential billing breakdowns with request-level granularity. Few do.

The 90-second timeout is conservative. Reasoning models can run longer. Extending it increases the window during which a crash leaves a reservation orphaned. The alternative is client-side streaming with server-sent events, which moves timeout responsibility to the client but complicates the accounting contract: when does the gateway release a held reservation if the client stops reading mid-stream?

Privacy Has Two Separate Boundaries

The gateway persists counters, not prompts or completions. Disable request-body logging at ingress and application instrumentation as well.

That does not establish end-to-end zero retention. Beacon intentionally retains memory; the harness and relay may retain additional data. Keep raw source code out of durable memory unless explicitly authorized, and verify upstream retention controls contractually.

The architectural benefit is narrower and useful: improving agent memory no longer grants the agent authority to increase its own spending limit.

The hardest production question this design leaves open: what happens when a seat exhausts its budget mid-sprint and the team has already committed to a delivery deadline? Do you manually bump the cap in the database, risking the same developer consuming the extension in another retry loop? Or do you require them to justify every additional dollar before you release it, turning yourself into a billing gatekeeper? I've done both. Neither felt right. What does your team do when an engineer's agent burns through a monthly budget in three days?