Inside agent-gov: Architecture of an Agent Cost Governance Platform Agent-gov is an open-source reverse proxy that intercepts every tool call made by AI agents, enforcing budgets in real time and auto-pausing out-of-control agents. Built as a FastAPI service with SQLite persistence, the proxy uses a four-stage decision tree — authentication, pause check, budget reset, and cost verification — to approve or reject calls before they reach external tools. The system runs 45 tests in 0.3 seconds and relies on a tool registry to determine actual per-call costs rather than trusting agent-reported estimates. AI agents orchestrate complex workflows — calling LLMs, scraping pages, querying databases, sending emails. Each call costs real money. Without a governance layer, a single buggy loop can burn through your budget before anyone notices. agent-gov is an open-source reverse proxy that intercepts every tool call your agents make, enforces budgets in real time, and auto-pauses out-of-control agents. Built as a FastAPI service with SQLite persistence, running 45 tests in 0.3 seconds. This post walks through the architecture: the proxy pattern, the four-stage decision tree, cost tracking with a tool registry, multi-tenancy via workspaces, and the lazy auto-reset pattern. Every AI agent tool call passes through agent-gov before reaching the actual tool. The agent sends a POST /proxy/call with its API key, tool name, and estimated cost. agent-gov validates, budgets, and logs — then returns a 200 to approve or a 429 to reject. class ToolCall BaseModel : agent key: str = Field ... tool name: str = Field ... estimated cost: float = Field 0.0, ge=0 The proxy doesn't execute the tool itself — it guards access. The agent only proceeds if the proxy returns 200. This is the gatekeeper pattern : a lightweight decision layer between the agent and the outside world. php Agent - POST /proxy/call - agent-gov - 200/429 - Agent decides | Calls actual tool | v OpenAI / Browser / API Why a proxy instead of a library? A library can be monkey-patched, removed, or forgotten. A proxy is a network boundary that agents must cross — it can't be bypassed. Every proxy call runs through a four-stage pipeline: python @app.post "/proxy/call" async def proxy tool call call: ToolCall : key hash = db.hash key call.agent key agent = await db.get agent key hash Step 1: Auth if agent is None: raise HTTPException status code=401, detail="Invalid API key" Step 2: Paused check if agent "paused" : raise HTTPException status code=429, detail=f"Agent '{agent 'name' }' is paused." Step 3: Auto-reset budget if new day agent = await db.check and reset budget agent Step 4: Look up REAL tool cost registered tool = await db.get tool call.tool name actual cost = registered tool "cost per call" if registered tool else call.estimated cost Step 5: Budget check new total = agent "spent today" + actual cost if new total agent "daily budget" : await db.pause agent key hash raise HTTPException status code=429, detail="Budget exceeded — agent auto-paused." Step 6: Approved — update spend and log updated = await db.update agent spend key hash, actual cost await db.log cost event key hash, agent "name" , call.tool name, actual cost return {"status": "approved", ...} | Stage | Check | Exit | |---|---|---| Auth | Does the API key hash match? | 401 — Invalid key | Pause | Is the agent paused? | 429 — Agent paused | Reset | New day since last call? | silent | Budget | Would this exceed the daily cap? | 429 + auto-pause | Log | INSERT cost event | 200 — Approved | The trickiest design decision was cost determination. Trusting the agent's estimated cost is fragile — agents can under-report. agent-gov uses a tool registry : an UPSERT-able table of known tools with real per-call costs. registered tool = await db.get tool call.tool name actual cost = registered tool "cost per call" if registered tool else call.estimated cost If the tool is registered, its true cost is used. The response includes a cost source field so clients know which path was taken. The test proves an agent can't lie its way past governance: an agent with a $100 budget claiming a $1 estimate for a tool registered at $500/call gets blocked with 429. v0.5 introduced workspaces — isolated tenants with their own agents, tools, and cost events. Each workspace gets a unique ID and API key. Every database row carries a workspace id FK column. Schema migration uses PRAGMA table info to add columns only when missing — SQLite doesn't support IF NOT EXISTS for ALTER TABLE . Tests verify workspace isolation: two workspaces, agents in each, neither can see the other's data. Instead of a midnight cron job creating a thundering herd, agent-gov uses lazy evaluation : every proxy call checks if a reset is needed. php async def check and reset budget agent: dict - dict: today = date.today .isoformat if agent "last reset" == today: return agent if agent "paused" : return agent return await reset daily budget agent "key hash" An agent that makes no calls doesn't need a reset. The thundering herd becomes a gentle trickle. The next evolution: per-tool budget caps, webhook-based alerts, and a management API. But the foundation — a simple, testable, async governance proxy — is solid. agent-gov is open source and MIT licensed. 45 tests. Zero database setup.