Security model for agents

The design assumes the model can be prompt-injected, merchants can lie, the remote server can be compromised and the agent's machine can be too. The ledger caps the loss at the agent wallet's balance.

OpenWallet has not yet had an independent security audit.

You can lose at most what you put in the agent wallet.

Who can do what

PartyCanCannot
The model or AI app, including prompt-injected content and poisoned toolsAsk OpenWallet to pay for a URL or a remote paid tool; see receipts and the balanceChoose a destination; exceed a limit; approve a payment; read a key; raise limits; pay on a network, asset or issuer outside the grant
A malicious merchant or 402Ask for any amount to any payToGet paid above the per-payment limit; get paid without the user's approval (remote) or a first payment without it (desktop); get a signed blob valid for more than about 80 s; get paid on another network
OpenWallet's remote MCP server, if compromisedDelay or drop requests; see paid content in transit; withhold a resourceSign anything (it holds no key); change the payee, amount, asset or network (the extension fetches the 402 itself and compares); approve a connection (only the extension can)
A phished authorise pageShow a connection codeConnect anything: the link happens only when the user approves in the extension, which shows the app's name and scopes
Malware running as the user on a desktop agent's MacCall the signer the way the MCP client does, within the limits; delete local stateRead the agent key (Keychain access control and the Secure Enclave wrap); raise limits (the grant is signed by the wallet); touch the main account; exceed the agent wallet's balance
Root on that Mac, or a stolen unlocked laptopDrain the agent wallet's balanceAnything on the main account. The user revokes from the extension
A facilitatorDelay or withhold submission; replay a settled blob to the merchantChange the amount or recipient (the signature covers them); replay across networks (separate accounts per network)

The remote server

  • No private key, seed, wallet part or grant key is ever on the server; a test checks the deployed image.
  • OAuth per the MCP authorization spec: PKCE, exact redirect-URI match, refresh-token rotation with reuse detection, token hashes only.
  • Per connection: at most 10 pending requests and 60 requests an hour; identical requests coalesce. The same merchant and amount more than 5 times in 10 minutes shows the user a warning and a "block this app" button.
  • Paid content passes through in transit and is not stored after delivery (large results: a download link deleted after 1 hour).
  • An append-only, hash-chained audit log records connections, approvals, declines and payments.

Keys

  • The desktop agent's key is generated inside the signer and never leaves it. There is no import and no export.
  • Seeds in environment variables are forbidden. No code path reads a key from an environment variable, an argument, a config file or stdin. openwallet-agent doctor warns if the environment contains *SEED*, *SECRET*, *MNEMONIC* or *PRIVATE_KEY* variables, and CI fails any example or template that sets one.
  • When the signer sees its RegularKey removed on the ledger, it marks the key revoked and deletes its Keychain item.
  • The signer is a Developer ID-signed, notarised app with the hardened runtime, which blocks debugger attach and library injection. The npm launcher checks that signature before starting it.

Approval channels

  • The extension (remote server): every payment, on the user's own device, after the extension has fetched and compared the price itself.
  • The operating system's prompt (desktop agent): macOS LocalAuthentication, Touch ID, Apple Watch or the login password. The model, an MCP hook or UI scripting cannot click it.
  • In-chat approval is not offered. MCP elicitation can be answered by the client or its hooks without the user, so it cannot guard money.
  • A desktop agent on a machine with no prompt fails payments that need approval with approval_required, and signs nothing.

Fetch rules (both servers)

  • DNS is resolved by the fetcher, which refuses loopback, private (RFC 1918), CGNAT, link-local (including 169.254.169.254), unique-local, multicast and IPv4-mapped forms of all of these, and connects to the address it checked, which defeats DNS rebinding.
  • TLS with the standard web roots. No custom CAs and no way to skip certificate checks.
  • No redirects to other hosts; at most 3 same-origin redirects; Authorization is dropped on any redirect.
  • 30 s per request, size caps on responses and challenges, at most 16 accepts[] entries, one payment per tool call.
  • Responses are data. OpenWallet never follows instructions from a body, a header or a description.

Protocol details that matter

  • A signed payment expires within 20 ledgers (about 80 s).
  • After a failed or ambiguous settlement, OpenWallet keeps reading the ledger for its own hash until the payment expires: it may have landed. It never signs a second blob for the same challenge.
  • A merchant that is paid on the ledger but does not deliver is flagged; after three, it needs approval again.

For the person

OpenWallet never asks for codes on a website. Type email and authenticator codes only inside the extension.

No MCP tool asks for or returns a seed, a password, a code or pairing material.

Report a vulnerability

Email security@opensync.network. See security.txt.