DEV Community

Komari venkata ramana
Komari venkata ramana

Posted on

Kage: Private Payments for Autonomous AI Agents

Private Payments for Autonomous AI Agents on Stellar

How a scoped session key + a Groth16 shielded pool let an agent spend without your key — and without publishing your counterparties.

An AI agent that can spend money on-chain is only as trustworthy as the key it holds. Hand it a raw private key and two things go wrong at once:

  1. Custody — the agent (or whoever compromises it) can drain the whole treasury. There is no "just a little" with an ed25519 key.
  2. Transparency — every payment the agent makes is a public record of who you paid, how much, and a running map of everything your treasury touches.

Kage fixes both, on Stellar, with two primitives that compose cleanly:
a scoped session account (autonomy without custody) settling through a
zero-knowledge shielded pool (privacy without a trusted relayer).

The problem, precisely

  • A scoped key that just caps spend still leaks to whom.
  • A private pool that hides recipients still needs a way to scope what an agent may pull.
  • On a transparent ledger, the two are usually solved by opposite designs (custodial scope vs. trustless privacy).

The two layers

1. Autonomy without custody — the SessionAccount

A Soroban account contract delegates one ephemeral agent key, and its
__check_auth permits exactly one thing:

  • Veil.deposit on the configured pool,
  • moving the configured token,
  • within a cumulative cap,
  • before an expiry.

The agent signs a Soroban auth entry with its session key — never the owner's key. The owner can revoke or extend at any time. Worst case for a compromised agent: it spends the remaining cap. It cannot drain, cannot redirect, cannot change the policy.

2. Privacy by math — the ZK shielded pool

Each payment is a deposit into a Tornado/Privacy-Pools-style pool, then a withdrawal to a fresh one-time stealth address:

  • Hides the recipient — Umbra-style stealth notes; each payee is paid at an address only the recipient can derive.
  • Hides the amount + link — a zero-knowledge membership proof breaks the deposit↔withdrawal trail. The proof shows "I own some unspent note" without revealing which.
  • Stops double-spends — a per-note nullifier reverts any replay on-chain.
  • Trustless tree — every deposit carries a Groth16 insert proof; the contract verifies the new Merkle root itself. No custodian.

Crucially, the contract verifies Groth16 on Soroban's native BN254 host functions — no hand-rolled pairing math in the wasm, and the amount is bound into the proof so a depositor can't commit a large note while under-funding the pool.

Why Stellar

  • Soroban native BN254 — the pairing check is a host function, so the ZK verifier is a few dozen lines of trust in the protocol, not a ported library.
  • Native-asset settlement — the pool runs on XLM via its Stellar Asset Contract, so there's no wrapped token, no bridge, no extra trust.
  • x402 / agent payments — Kage slots into the x402 "agent pays for its own API calls" model, giving autonomous agents a private, scoped payment rail.

What's live

A testnet deployment runs the full flow end to end — deposit (insert proof verified, USDC/XLM pulled), withdraw (membership proof verified, paid to a stealth address), and a rejected double-spend attempt. On mainnet, the same contracts settle in XLM.

The honest scope

This is not the full two-key Umbra scheme, and Groth16 means a trusted setup (phase 1 reuses the Hermez powers-of-tau). The deposit amount is published in the event (needed for the amount-binding check), so fixed-size notes are the recommended practice. See the repo's security review for the full ledger of limits.

Kage is open source (MIT). Repo: github.com/Venkat5599/kagezks

Top comments (0)