Spend caps and BYOK: paying without handing over keys
A self-custodial x402 payer keeps the signing key on the operator's machine — which means the spend caps and the price check have to be code the operator runs.
By Matthias Begot ·
An agent that pays for APIs raises three separate questions, and they are worth keeping separate: who holds the signing key, what bounds the spend, and who checks that the amount demanded is the amount published. A managed wallet platform answers all three by moving them into its own infrastructure. A self-custodial payer — bring your own key, signing locally — keeps the key on the operator’s machine and therefore has to answer the other two questions in code the operator runs. This article describes what that code has to do, and states plainly what it cannot do.
Three articles on this blog now cover adjacent ground and are deliberately not the same subject: machine-readable discovery is about how an agent finds a paid endpoint; managed agent wallets is about custody handled by a platform, seen from the seller’s side. This one is about custody and guardrails on the client side — the controls nobody can enforce on the agent’s behalf.
What an x402 signature actually authorizes
The mechanics decide the risk model, so start there. In the exact scheme on EVM
— the one nearly every deployment uses — the payer signs an EIP-3009
transferWithAuthorization payload. Per the
scheme specification,
the authorization object carries from, to, value, validAfter,
validBefore and a random nonce, and the resulting signature is one-time
use, bound to a fixed amount, inside a validity window. The facilitator
submits the transaction and pays the gas; the payer never touches the chain.
Two consequences follow, and they point in opposite directions.
The reassuring one: this is not a standing allowance. Unlike an ERC-20
approve, a signed x402 authorization cannot be drawn down repeatedly, and it
cannot be inflated by the facilitator — the amount is committed in the signed
typed data. Exposure per signature is exactly the amount in that signature.
The uncomfortable one: once the signature exists, the payer controls nothing. It is a bearer authorization. There is no “cancel” between signing and settlement, and cumulative exposure across a session is simply the sum of every amount the client agreed to sign. Which means the only real control a self-custodial payer has is the decision to refuse to sign — and that decision has to be made before the signature is produced, deterministically, outside the model.
Two custody shapes, stated without a verdict
| Managed wallet | Self-custodial (BYOK) payer | |
|---|---|---|
| Key location | Custodian (CDP, Privy, Fireblocks…) | Operator’s machine, in the payer process |
| Policy enforcement | Platform infrastructure, outside the agent | Client code the operator runs |
| Audit trail | Centralized, tied to the agent run | Whatever the operator’s host logs |
| Revocation | Revoke delegated authority at the custodian | Stop the process; move the funds |
| Blast radius on compromise | Bounded by the delegation policy | Bounded by the wallet’s balance |
| Dependency | The platform must be up, and must support the rail | None beyond the gateway |
Neither column is the safe one. A custodian gives you revocation and a real audit trail, and gives you a third party who can be down, can change terms, or can be compromised. Self-custody removes that party and hands you the entire operational burden: the key is on a machine you administer, and the spend policy is only as good as the code enforcing it. Most operators will pick by which failure they can actually live with, not by which architecture reads better.
What does not change between them is the wire protocol. Both produce the same
request, the same 402, the same signed replay — the loop described in
What happens between a 402 challenge and the paid response.
The three checks that belong before a signature
A self-custodial payer that only enforces “the user set a limit somewhere” is not enforcing anything. Three independent checks are worth running on every call, each of which can only be done at the moment of signing, and each of which should be a hard refusal rather than a retry.
1. The challenge amount versus the published price
The seller announces prices on its discovery surfaces; the seller also writes the
amount into the 402 challenge. Those are two different statements, and a payer
that never compares them has taken the second one on faith. Comparing them is
what catches a forged challenge, a compromised gateway, or a mis-priced endpoint
— before any signature exists.
This is not a hypothetical worry. The May 2026 study Five Attacks on x402 Agentic Payment Protocol (Li, Wang & Wang) documents payer-side failure modes across the HTTP–chain boundary, and its guidance to payers is direct: verify that the requested amount matches published pricing rather than trusting the challenge alone. The same paper’s discovery-layer attack — adversarial or Sybil registry entries steering agents toward attacker-controlled endpoints — is the reason this check matters even when the protocol behaved correctly: an agent can be steered to the wrong seller entirely, and a price check is the last thing standing between the steering and the signature.
For a flat price the comparison is trivial. For a per-unit endpoint it is not: the
legitimate amount is base + N × unit, where N is the number of items in the
body the client is about to send, floored and capped by the published bounds. A
payer that wants this check on batch calls has to recompute the expected price
from the body it is sending — the mechanism is dissected in
One settlement, N units.
2. A per-call ceiling
A per-call cap is the answer to a single catastrophic signature — the mis-parsed decimal, the endpoint that changed price by three orders of magnitude, the challenge that asks for an amount no endpoint in the catalog charges. It is a blunt instrument and that is the point: it does not need to understand the call to refuse it.
Doing the arithmetic in integer atomic units rather than floats is not pedantry here. Prices in this space run to fractions of a cent, token decimals differ per asset, and a rounding artifact in a comparison is a guardrail that silently stops guarding — a failure mode covered from the seller’s side in Token decimals: why an x402 rail stays switched off.
3. A session budget
The per-call cap bounds one signature; nothing in it bounds a loop. An agent that retries a failing task, or a task decomposed into far more sub-calls than anticipated, spends within the per-call cap every single time. A cumulative budget for the process lifetime is the only check that sees the aggregate.
One design detail decides whether the budget is honest: count at signature, not at settlement. A payment whose settlement failed still exists as an authorization someone may submit; treating it as unspent is optimism, not accounting. The corollary is that a re-presentation of the same authorization — after a failed settle — must not be counted twice, or a client near its budget refuses calls it already paid for.
Where the key lives, concretely
The Invoket MCP server (@invoket/mcp) is one implementation of the shape
described above, and its choices are auditable in the open — the mechanisms below
are documented in its
threat model and in
the MCP docs page.
- The key is read from the process environment, once, at startup. Not from argv, not from a config file the package ships, not from the repo.
- The environment variable is then deleted from
process.env. A later state dump, a log of the environment, or a spawned subprocess no longer carries it. - What survives is a signing account whose key lives in closures — never a readable property of an object that could be serialized into a tool result.
- A global output scrubber redacts key-shaped material from every log line and every error message, as a backstop rather than as the primary defense.
- Nothing is written to disk, and there is no telemetry. The spend counters live in memory and die with the process — which also means they reset on restart, a limitation stated below rather than buried.
- One configured origin, no arbitrary fetch. Every request goes to the single gateway origin; no model-supplied input can redirect it elsewhere.
- Without a key, the server starts in discovery-only mode. The catalog and the schemas are browsable; paid tools refuse with an actionable message instead of failing obscurely.
Running over stdio is a security property, not just a packaging choice: the
MCP specification (revision 2026-07-28,
Security Best Practices)
recommends that locally-run servers “use the stdio transport to limit access to
just the MCP client”, precisely to prevent other local processes from reaching a
server that holds capabilities. A payer process is exactly the kind of server that
recommendation is about. The same document is candid that local servers are
attractive targets in general — which is the honest framing for anything holding
a key on a developer machine.
The flow, end to end
What actually happens on a paid call, with the guardrails in place:
- The MCP host launches the payer process over stdio and passes the key and the caps through the environment. Nothing is installed and nothing is hosted remotely.
- The payer reads the gateway’s discovery surfaces and derives one typed tool per paid endpoint — no hard-coded endpoint list, so the tool set follows the catalog. See For agents for those surfaces.
- The agent calls a tool. The payer sends the request unpaid first.
- The gateway answers
402with the challenge: rail, asset, network, amount. - Before signing, the three checks run against the challenge: expected rail and asset, amount versus the published price, per-call cap, session budget. Any breach is a refusal that names the variable to adjust — never a silent retry, never a partial signature.
- The payer signs the EIP-3009 authorization locally and replays the request with the payment attached.
- The gateway settles and returns the result. A
2xxwith no payment response means the call was not charged — a free call or a trial, reported as such, per the settlement line and the zero-amount trial rail.
The seller saw an ordinary x402 exchange, identical to the one in the Quickstart. Nothing on the wire reveals where the key was, and nothing needs to.
What spend caps do not protect against
A guardrail article that only lists guardrails is marketing. These are the limits, stated as such.
- Caps bound spend, not exposure. The hard ceiling is the balance of the wallet whose key you provided. Every serious deployment uses a dedicated, low-balance payer wallet; the caps are a convenience on top of that, not a substitute for it.
- An in-memory session budget resets on restart. A host that respawns the process resets the counter. Anyone who needs a true daily or monthly ceiling needs it enforced somewhere that outlives a process — which is one of the things a managed wallet actually sells.
- A cap cannot tell a good call from a bad one. A compromised or prompt-injected agent that stays under the per-call cap spends the whole session budget on calls that were never wanted. Caps limit the damage; they do not detect the intent.
- A price check is not a trust check. Verifying the amount against a published price proves the seller charged what it advertised. It says nothing about whether the endpoint was worth calling, or whether discovery steered the agent to the wrong seller in the first place.
- Settlement-path risks are protocol-level, not client-level. The x402 attack study describes charged-but-unserved outcomes and replayed grants across the HTTP–chain boundary. Those are properties of how a seller and its facilitator handle idempotency and finality; no client-side cap fixes them, which is a reason to care which sellers you buy from.
- Self-custody has no revocation story. There is no third party to call. If the key is exposed, the response is to move the funds — and that is on the operator, at operator speed.
Choosing between the two
The useful question is not “which is more secure” but which failure you are staffed to handle. Reach for a managed wallet when you need revocation, a centralized audit trail, or spend policy that survives a process restart and spans many agents — and accept a dependency and a counterparty. Reach for a self-custodial payer when you want no third party in the payment path, a small auditable surface, and a wallet whose balance is the policy — and accept that the operational burden is entirely yours.
What both share is worth ending on: the seller is not part of this decision. A
standard x402 endpoint is payable either way, which is exactly why the custody
question belongs to the operator running the agent, and to nobody else. The prices
and rails that either client checks against live on the gateway’s
/catalog — this article, per house rule,
quotes none of them.
Sources
- Coinbase x402 —
exactscheme on EVM (EIP-3009 payload, one-time-use signature, fixed amount, validity window). - Zelin Li, Qin Wang, Zhipeng Wang — Five Attacks on x402 Agentic Payment Protocol, arXiv:2605.11781 (May 12, 2026): settlement-path inconsistencies, replay across the HTTP–chain boundary, proxy/cache confusion, and discovery-layer steering, with payer-side mitigations.
- Model Context Protocol — Security Best Practices, specification revision 2026-07-28: local MCP server compromise, stdio transport restrictions.
- Invoket —
@invoket/mcpthreat model and MCP server documentation.