Safe-mainnet switches
Three env-var switches operators can flip to bound mainnet exposure — spend cap, allowlist, and kill switch.
VDM Nexus runs without a paid audit. The /v1/chat/completions route ships
with three operator-controlled switches that bound the blast radius on
mainnet. Together they let an operator flip the rail off in seconds, cap
the maximum charge per request, and limit which payers can spend.
All three switches live as env vars on the apps/nexus deployment. None
require a code change or redeploy — set them in the Vercel dashboard
("Environment Variables" → "Production") and trigger a redeploy from the
deployments tab, or wait for the next push.
The three switches
| Env var | Effect | Default | When to use |
|---|---|---|---|
NEXUS_MAINNET_ENABLED | Set to false to fail-closed on every mainnet request with 503 mainnet_disabled. Testnets stay reachable. | Mainnet enabled | Incident response — flip off when anything looks wrong on-chain. |
NEXUS_MAX_PRICE_USDC | Hard ceiling on the flat price advertised in 402 challenges. The route refuses to issue a challenge above this cap. | 0.10 USDC | Always-on safety net against a typo in X402_FLAT_PRICE_USDC. |
NEXUS_ALLOWED_AGENTS | Comma-separated payer pubkeys. When set, non-listed payers get 403 agent_not_allowed. | Unset (allowlist disabled) | First days on mainnet — narrow the rail to known agents before opening up. |
NEXUS_MAINNET_ENABLED — the kill switch
NEXUS_MAINNET_ENABLED=falseWhat happens:
- Every request to
/v1/chat/completionswhoseX402_NETWORKlooks like a mainnet (anything not containingdevnet,testnet,sepolia,goerli, orholesky) returns503 mainnet_disabledwith a one-linemainnet.disabledlog event. - Devnet, Sepolia, and other testnets are unaffected.
- Probes (no
X-PAYMENTheader) are blocked too — no challenge is issued while the switch is off, so a paid client can't even start the handshake.
Fail-closed by design: a missing or unrecognised network identifier is treated as mainnet, so a typo can't bypass the switch.
Default behaviour: any value other than the literal string false (or an
unset variable) leaves the switch on. This way an accidentally cleared env
var doesn't take prod offline.
NEXUS_MAX_PRICE_USDC — the spend cap
X402_FLAT_PRICE_USDC=0.01
NEXUS_MAX_PRICE_USDC=0.10What happens:
- Before the 402 challenge is built, the route compares
X402_FLAT_PRICE_USDCagainstNEXUS_MAX_PRICE_USDC. - If the configured price exceeds the cap, the route returns
500 server_misconfiguredwith aprice.over_caplog event carrying both values. No challenge is advertised. - A missing or invalid
NEXUS_MAX_PRICE_USDCfalls back to0.10USDC.
Set this in production to whatever your largest intended per-call charge
is. The default of 0.10 USDC is two orders of magnitude above the v1 flat
fee of 0.01 — tight enough to make a fat-finger to 1.0 or 10.0
unable to settle.
NEXUS_ALLOWED_AGENTS — the allowlist
NEXUS_ALLOWED_AGENTS=8VK2...,5pCq...,9TwR...What happens:
- When set, the route extracts the
payerfield from the decodedX-PAYMENTpayload and checks membership against the allowlist. - A non-matching (or missing) payer returns
403 agent_not_allowedwith anagent.not_allowedlog event including the claimed pubkey. - When unset or empty, the allowlist is disabled (the default) — every payer is accepted, subject to the usual rate limits.
The check runs before facilitator verify/settle, so a blocked payer doesn't get their payment broadcast.
Logging
Each switch emits a distinct event so Vercel log search tells you immediately what's blocking traffic:
| Event | Level | Switch | Carries |
|---|---|---|---|
mainnet.disabled | warn | NEXUS_MAINNET_ENABLED | request_id, network |
price.over_cap | error | NEXUS_MAX_PRICE_USDC | request_id, configured_usdc, max_usdc |
agent.not_allowed | warn | NEXUS_ALLOWED_AGENTS | request_id, network, agent_pubkey |
sol_balance.low | warn | fee-payer drainage | request_id, address, network, lamports, sol |
sol_balance.critical | error | fee-payer drainage | request_id, address, network, lamports, sol |
The allowlist check is mainnet-only. When NEXUS_ALLOWED_AGENTS is
set, devnet/Sepolia payers are unaffected — so the operator can lock
down the first days of mainnet without breaking the public devnet
playground.
The SOL-balance probe runs on the Vercel Cron schedule every 5 min
(/api/v1/ops/sol-balance). It queries getBalance for
NEXUS_DEPOSIT_ADDRESS and emits sol_balance.low at < 0.10 SOL,
sol_balance.critical at < 0.02 SOL. Vercel log search
(level:warn event:sol_balance.low) is the alert surface — pair with
a log drain if you want push notifications.
See Observability for the full event vocabulary and Vercel-log search recipes.
Recommended mainnet flip
A safe sequence for promoting the rail to mainnet:
- Set
NEXUS_MAINNET_ENABLED=falsebefore changing the network. The switch fails-closed for unrecognised networks, so flipping it off first means no mainnet request can sneak through during the network change. - Set
NEXUS_ALLOWED_AGENTSto your own wallet(s). The rail is now reachable only by you. - Tighten
NEXUS_MAX_PRICE_USDCto whatever you actually intend to charge —0.02, not the default0.10. - Change
X402_NETWORKto the mainnet identifier and redeploy. - Set
NEXUS_MAINNET_ENABLED=true(or clear it — the default is enabled). Run a paid call against your own wallet end-to-end. Verify the receipt at verify.vdmnexus.com. - Once stable, broaden
NEXUS_ALLOWED_AGENTSor clear it to open the rail to public traffic.
If anything looks off at any point: flip NEXUS_MAINNET_ENABLED=false
and redeploy. You're back to a known-safe state in under a minute.