Privacy: making public metadata uninformative
The same public ledger that lets strangers trust each other also lets a competitor watch your agent's vendor spend in real time. Two layers blunt that without breaking what makes the ledger worth trusting: hygiene defaults in the SDK and node, and the bonded privacy relay. It matters more when an agent finds tools on its own: the services it pays are chosen mid-run, so the shape of your spend is not a list you wrote down but a trail the agent leaves as it works.
The framing
Escrow amounts, settlement events and timestamps on a public chain are inherently public. That transparency makes strangers safe to transact, and it stays. What changes is what the public graph reveals: not hiding activity but making attribution expensive and the metadata graph uninformative. A competitor cannot watch "your" spend if they cannot cheaply tie addresses to you or profile workloads from amounts and timing.
API keys are not "private" either; they are private-to-one-party, because your provider sees everything. Two layers let you choose where on that spectrum to stand, per relationship.
Layer 1 — hygiene defaults (@aioum/sdk + node)
The first layer asks nothing of you; most of it is on by default. Three mechanisms each remove one signal from the public graph:
| Mechanism | What it hides | Where |
|---|---|---|
Deposit bucketing — explicit session deposits round UP to a 1/2/5×10ᵏ ladder (default ON, privacy: { bucketDeposits: false } to opt out; capped by the agent's budget) | escrow sizes stop fingerprinting workload intensity — every 10-USDC deposit looks like every other | bucketDeposit(), wired into AIOUMAgent.openSession |
Per-relationship accounts — accountSalt: saltForRelationship(owner, label) derives a distinct (recoverable, deterministic) smart account per vendor | one owner's relationships appear as unrelated addresses | createAgent({ accountSalt }) |
Settle-timing jitter — random delay before an auto-enqueued settle's first attempt (AIOUM_SETTLE_JITTER_MS, default 30s; manual settles never jittered) | settlement timestamps stop correlating tightly with usage bursts | node settle loop |
Each has a cost. Bucketing consumes budget headroom, never money, since the remainder refunds
at settle. Per-relationship accounts fragment client reputation and add setup gas. And the
funding trail persists: owner→account transfers are public, so per-relationship accounts
raise attribution cost rather than erase it; fund from pooled sources for the full benefit. The
one-off agent.call() path needs no bucketing: its deposits already equal the service price,
uniform across all users.
Layer 2 — the bonded privacy relay
The second layer is for the relationship where hygiene is not enough and you want the service itself not to know who is calling. A relay is just an AIOUM operator whose service is "proxy any other AIOUM service." No new contracts; every existing guarantee applies unchanged: it stakes ZAI, its escrow is exposure-capped, its sessions are disputable, its stake is slashable, and it anchors call transcripts like any node.
client ──escrow session──► RELAY ──its own escrow sessions──► target services
The chain records client↔relay and relay↔targets. It never links client↔target, and
npm run demo:relay demonstrates this by enumerating the full on-chain session graph.
The trust deal: the relay sees your usage. That is the point: one chosen, staked, slashable party instead of the world, the API-key privacy deal except that you pick the party, can rotate it, and it posts collateral. The relay still cannot overbill, because you sign every voucher, and you keep dispute recourse against its stake.
You pay for the relay per call: the target price plus AIOUM_RELAY_MARGIN_BPS (default 30%).
The relay's own settlements pay the protocol fee and delegator split out of gross, so
break-even is ≈ 17.7% at default splits, and 30% nets ≈ 10% of target price. Quotes come from
GET /quote?serviceId=. The on-chain pricePerCall is only the minimum-deposit floor; x402
communicates the real per-call price.
The relay runs as npm run relay (offchain/src/relay/server.ts), with the same hardening,
durable stores, settle loop and verifiability as the node runtimes. Its targets come from
AIOUM_RELAY_TARGETS, its downstream sessions are auto-opened and rotated from the relay's
wallet (AIOUM_RELAY_DEPOSIT), and its voucher totals self-heal from 402 challenges after a
restart. On your side, agent.openSessionVia(relayUrl, { serviceId, tool }) returns a
RelaySession whose wrapped relay_call invocations run at the quoted price; evidence
packages work unchanged, because client and relay hash the same wrapped request. Relays never
chain through other relays: no free multi-hop, a deliberate simplicity choice.
What stays public
Amounts and events stay public at the settlement layer: the subgraph, reputation and dispute systems depend on them, so AIOUM uses no shielded escrow pools and hides none of the events those systems read. The rating your agent issues after every settled session is public on purpose; it is only useful to the next agent because it is visible. Privacy comes from making attribution expensive, not from hiding settlement.
What an observer still sees
Even with both layers, an observer sees aggregate flows through relays, relay operators' own graphs, funding transactions and anything a subpoenaed relay reveals. Layers 1+2 turn "the world watches your vendor spend live" into "a party you chose and can slash knows, and the world sees noise". That is the deliverable, not anonymity.
All of it assumes you hold USDC in a wallet and pay your own gas. Many of the people who want to deploy an agent have a card and no wallet at all.
Next: Managed billing — how a person with a card and no wallet ends up with the same on-chain sessions and receipts as everyone else.