An account per agent,
with keys it never sees.
Agents leak. They paste their context into logs, into tools, into other models. So the wallet is built on the assumption that anything the agent can read is already public: it holds a handle, and the signature happens somewhere it cannot reach.
import { createWallet, localSigner, memoryRail } from "@moneolabs/wallet"; const wallet = await createWallet({ agent: "research-agent-01", signer: localSigner(), rail: memoryRail(), asset: "USDG", guard, funding: "500 USDG",}); // The agent receives a handle. There is no key to leak.// → wallet.id "wlt_3m9Qz…"// → wallet.address "0x4a91…c7d2"Pick how much of the key you want to hold.
Keys are generated and used inside an attested enclave. Moneo can sign on the agent's behalf but cannot export the key, and attestation is verifiable from your side.
Three shares: one held by you, one by Moneo, one in cold backup. Two must cooperate to sign, so neither party can move funds alone.
Point the SDK at your own KMS or HSM. Moneo builds and simulates the transaction, your infrastructure produces the signature.
Eight calls do the work.
- Agent
- research-01
- Custody
- tee-signer
- Network
- robinhood-mainnet
- Balance
- $486.32
- 24h spent
- $119.68
- Key exposure
- none
Policy runs between intent and signature.
The guard ships inside the wallet and runs before every signature. A refusal is a structured value with a reason, never a fee, and every verdict lands in the decision ledger with the policy version that decided.
A ceiling per transaction, per counterparty, per asset class. The smallest useful policy is one line and stops the worst outcome.
Rolling windows rather than calendar months. A stuck retry loop burns the budget, then stops, instead of the balance.
Counterparties by address, domain, x402 endpoint, or merchant ID. Deny by default is the sane setting when the caller is a language model.
Rate limits on money, not requests. Ten payments a minute to the same address is a bug signature, whatever the amounts look like.
Above a threshold, the action pauses and a human approves it in Slack, email, or your own webhook. The agent gets a pending handle, not a failure.
Replay a proposed policy against last month's ledger before shipping it. See what would have been blocked, and what it would have cost you.
Decision ledger
Execution with the exit attached.
Tokenized equities on Robinhood Chain, quoted across verified venues, with every fill routed through the same guard as a payment.
Every order carries a maximum. Exceeded means rejected, never silently filled worse.
Market, limit, TWAP, and bracket — entry with the take-profit and stop attached at submission, so an unattended run has an exit.
Concentration limits per symbol and per agent, enforced at submission time.
Notional passes the spend guard before signing, exactly like a payment does.
Why not just hand it the exchange API?
Because the API assumes the caller is you. An agent trading unattended needs a layer that assumes the opposite: that the caller leaks, loops, and never sleeps.
Raw exchange API · An API key with full account access sits in the model's context, one pasted log away from public.
Behind Moneo · The model holds a handle. The key lives in a TEE or an MPC quorum it cannot read from.
Raw exchange API · A stuck retry loop trades until the balance is gone or you wake up.
Behind Moneo · The loop burns its rolling budget, then every attempt comes back blocked, with a reason.
Raw exchange API · Whatever the account can afford is what the agent can concentrate into one symbol.
Behind Moneo · Per-symbol and per-agent caps are enforced at submission, before any order exists.
Raw exchange API · Thin liquidity fills you anyway, and you find out from the statement.
Behind Moneo · Every order carries a slippage bound. Exceeded means rejected, never filled worse.
Raw exchange API · Stop logic is code you write, hosted somewhere that has to stay awake.
Behind Moneo · Brackets attach the take-profit and the stop at submission. The exit ships with the entry.
Raw exchange API · A list of fills. What the agent tried and was refused is nowhere.
Behind Moneo · Every attempt, verdict and reason included, attributable to one agent and one policy version.
Four steps from an agent that asks to an agent that pays.
Nothing here is a migration. Each step is a call you add next to the code you already have.
Write the policy
Declare what this agent may do with money: caps per transaction, a rolling budget, who it can pay, what it can trade, and the threshold where a human has to say yes.
Issue the wallet
One call creates a funded account for a named agent, with the policy already attached. Custody is a TEE signer, an MPC quorum, or your own KMS. Nothing about the key material crosses into the model's context.
Hand it the tools
Expose the SDK to your agent over MCP or plain function calling. It sees pay, swap, quote, balance, and gets a structured refusal when policy says no.
Read the ledger
Every attempt is recorded with its verdict and reason, attributable to one agent, one run, one policy version. Reconciliation stops being archaeology.
Roughly twenty lines to a funded agent.
Start on the sandbox ledger with test funds. Flip one flag when you want it real.