One environment variable ran three different powers on our payment stack: it moved escrowed USDC on mainnet, it signed the verdicts that tell buyers an agent did the job, and it stamped operational attestations. One env dump, one debug endpoint, one RCE — and whoever leaked it owned all three at once.
The god key
ARC_EVALUATOR_KEY was three keys in one trench coat:
- Money — evaluator authority on escrow settlements. Drains real funds.
- Ops — attestations, anchor writes, venue evaluation calls.
- Verdict — the signature behind every public verdict.
A /proc/<pid>/environ dump gets all three in one read.
The fix: split + socket
Three secrets replace one: ARC_SETTLE_KEY (money), ARC_OPS_KEY (ops writes), ARC_VERDICT_SIGNER_KEY (verdicts). The money key moves into a second process on the same host — a small Bun signer service that answers over a Unix socket file (NDJSON-RPC, no TCP, zero network surface).
# API child starts with the money key REMOVED from env:
exec env -u ARC_SETTLE_KEY -u SIGNER_KEY bun src/server/index.ts
The API talks through an ArcSigner seam — same interface as the env backend, so callers can't tell which is wired. A policy layer allowlists target contracts and caps gas: even a compromised API can only request operator-approved signatures.
Fail fast, never fall back
Signer dead → 503 signer_unavailable before x402 payment runs — you can't be charged for a service that can't sign. Free routes keep serving. Operator gets a Discord/Telegram alert, throttled to one per 5 minutes. No silent fallback key — that would recreate the god-key.
Rotation without breakage
GET /keys/verdict-signers.json publishes {current, next} evaluator addresses — electron-builder's trust model. Verdicts are EIP-712 self-describing: recover the signer, compare to the embedded address. Retired-key verdicts verify forever.
Honest tradeoffs
- Same-host RCE can still request signatures — policy bounds it, doesn't eliminate it.
- Ops/verdict keys still live in env — money moved first; migration planned.
- +1 process, ~$0 recurring.
When KMS/MPC
When volume makes self-hosting the risky option, or compliance requires non-exportable keys. AWS KMS, Web3Signer, Circle dev-controlled wallets (verified: ARC support + EIP-712) — the seam accepts all three without changing callers.
Check your own processes
tr '\0' '\n' < /proc/<pid>/environ | grep -iE 'key|secret'
Whatever prints is what an RCE inherits.
Full article: https://agentbadge.xyz/blog/keyless-signer
Top comments (0)