ledger-agent
v0.1.1
Published
Governance wrapper for x402 payments — spend caps, transaction logs, and anomaly detection for AI agent micro-transactions.
Downloads
133
Maintainers
Readme
ledger-agent
Governance wrapper for x402 payments — spend caps, transaction logs, and anomaly detection.
What is this?
A drop-in fetch replacement for AI agents that transact via the x402 protocol. It enforces spend caps before any payment leaves the agent and captures a structured record of every transfer — so a runaway agent can't burn through the budget and every paid call is reconstructable after the fact.
For teams deploying agents that spend real money on paid APIs.
Features
| Feature | What it does |
|--------|--------------|
| Spend caps | Per-call, hourly, daily, and lifetime USD limits. Rejected before the paid request. |
| Transaction records | Every outcome (success, blocked, error) emitted with id, agent, endpoint, amount, tx hash, timestamp. |
| Zero runtime deps | Just Node 18+ (global fetch). No hidden network. |
| Dual build | ESM + CJS, with TypeScript declarations in the package. |
| Pluggable fetch | Pass your own fetch for tests, proxies, or edge runtimes. |
Install
npm install ledger-agentNode 18+ (or any runtime with a global fetch).
Quick start
import { createLedgerAgent } from "ledger-agent";
const agent = createLedgerAgent({
agent: "researcher-01",
budget: {
perCall: 0.05,
hourly: 25,
daily: 100,
lifetime: 500,
},
onRecord: (record) => {
console.log(
"[governance]",
record.status,
record.endpoint,
`$${record.amount.toFixed(3)}`,
);
},
});
const { response, record } = await agent.fetch(
"https://api.example.com/data",
);
console.log(await response.json());
console.log("spent so far:", agent.snapshot());How it works
agent code
│
▼
agent.fetch(url)
│
├── first request ────────────► server
│ │
│ ▼
│ 402 Payment Required
│ x-payment-amount-usd: 0.002
│ │
▼ ▼
check budget caps ─────► BudgetExceededError (blocked, emitted, thrown)
│ ok
▼
retry with x-payment-authorization ─► server ─► 200 OK
│
▼
emit TransactionRecord ─► onRecord()
│
▼
return { response, record }Non-402 responses pass through untouched (with a zero-amount record emitted for the audit trail).
API
createLedgerAgent(options): LedgerAgent
| Option | Type | Description |
|--------|------|-------------|
| agent | string | Identifier for the calling agent. Required. |
| budget.perCall | number? | Max spend per single call, in USD. |
| budget.hourly | number? | Max spend per rolling hour, in USD. |
| budget.daily | number? | Max spend per rolling day, in USD. |
| budget.lifetime | number? | Hard cap across the lifetime of this governance instance, in USD. |
| onRecord | (r) => void \| Promise<void> | Called after every transaction. Use to write into your log store. |
| fetch | typeof fetch | Custom fetch implementation. Defaults to globalThis.fetch. |
agent.fetch(input, init?): Promise<PaymentResponse>
Same signature as the global fetch, but returns { response, record }.
agent.snapshot()
{ agent: string, total: number, hourly: number, daily: number, budget: BudgetLimits }BudgetExceededError
Thrown before the paid request is sent when any cap would be breached. Fields: kind (which cap), limit, wouldSpend.
Tech stack
- TypeScript 5
- tsup — ESM + CJS + declaration output
- Node 18+ global
fetch - Zero runtime dependencies
License
MIT
