Mainnet safe-flip hardening: boot guards + honest discovery surfaces#3
Mainnet safe-flip hardening: boot guards + honest discovery surfaces#3Sarthib7 wants to merge 5 commits into
Conversation
Refuse to boot on NETWORK=mainnet with the demo TEMPO_RECIPIENT, the dev MPP_SECRET_KEY, or a payee key that mismatches the recipient (every cooperative close would fail the on-chain payee check). Warn loudly when no payee key is set. /llms.txt is now generated from the live chain config and /SKILL.md gets a live-network banner, so agent discovery can never contradict the 402 challenge. currencyName follows the on-chain symbol (USDC.e); demo agent messages are network-aware.
The static agent/human surfaces claimed 'testnet only, pathUSD, no real money' unconditionally — on a mainnet deployment they would tell paying agents the opposite of the truth. State both networks, teach agents to verify the live chain via GET / -> network.chainId, and keep the testnet verified-runs sections labeled as historical evidence.
Drop vars no code reads (UPSTREAM_URL/UPSTREAM_API_KEY/PRICE_PER_CALL), add the ones it does (TEE_ENDPOINT, EXPECTED_MEASUREMENT, CHUNK_COUNT, MAX_DEPOSIT, MAX_BODY_BYTES, LOG_LEVEL), document the mainnet boot guards and stablecoin-gas payee funding. render.yaml: production recipient address + keep the payee key dashboard-only.
Adversarial review (24-agent, live-reproduced) found the guards checked exact strings only: - secret guard now rejects the .env.example placeholder, anything under 24 chars, and both known dev strings (challenge provenance is HMAC over this secret — a public value makes it forgeable) - recipient guard now trims and requires a valid address, so empty, '0x…'-placeholder and whitespace-padded values fail closed - NETWORK is normalized (trim+lowercase) and CLOSED: unknown values throw instead of silently running testnet; boot log states network/chainId/currency - SKILL.md banner injection falls back to after-frontmatter/top if the H1 anchor ever drifts (the REAL MONEY notice must never vanish) - leftover unconditional pathUSD/testnet claims fixed: SKILL.md frontmatter + troubleshooting (and the nonexistent --account flag), dynamic llms.txt verified-runs block, landing hero mock, #verified lede, footer explorer links, CLI/MCP labels now use the live currencyName - .env.example: MAX_DEPOSIT documented as read by BOTH sides (server suggestedDeposit in 402 challenges), CHUNK_COUNT 'up to N ticks'
|
Adversarial review pass complete (24 agents, every finding live-reproduced before being accepted): 19 confirmed findings, all fixed in 579054d. Highlights — all three were real, boot-reproduced bypasses of the original guards:
Full re-verification after the fixes: 6 fatal scenarios + uppercase-MAINNET normalization + garbage-value crash + testnet regression, all green. |
|
Staging verification (repo protocol step 4b) — PASSED on this branch. Full paid E2E on Tempo Moderato testnet against the real Phala enclave, run from the
|
evidence.md gains the 2026-07-14 staging run: paid E2E on the hardening branch against the real enclave, and the first on-chain cooperative close (payee nonce 0->1, +0.0002 revenue - ~0.000589 close fee), with the per-session economics warning. README Status/TODO/config table now reflect verified mainnet readiness and the remaining cutover steps.
What
Prepares the relay for a mainnet (Tempo Allegro, chain 4217, USDC.e) cutover without touching payment logic. PR #1 added the
NETWORKswitch; this PR makes flipping it safe:Boot guards (fail-closed on real money) — on
NETWORK=mainnetthe server now refuses to boot when:TEMPO_RECIPIENTis still the built-in demo address (real USDC.e would settle to it)MPP_SECRET_KEYis still the dev defaultTEMPO_RECIPIENT_PRIVATE_KEYaddress ≠TEMPO_RECIPIENT(every cooperative close would fail the on-chain payee check — silently, per request)plus a loud warning when no payee key is set (settlement degrades to payer timeout-reclaim). Testnet behavior is unchanged.
Honest discovery surfaces —
/llms.txtis now generated from the live chain config and/SKILL.mdgets a live-network banner, so what agents read can never contradict the 402 challenge. The static skill/landing/llms files no longer claim "testnet only, no real money" unconditionally; they state both networks and teach agents to verify viaGET /→network.chainId.currencyNamenow matches the on-chainsymbol()(USDC.e, verified byeth_call).Ops docs —
.env.examplenow lists exactly the env vars the code reads (droppedUPSTREAM_URL/UPSTREAM_API_KEY/PRICE_PER_CALLwhich nothing reads; addedTEE_ENDPOINTetc.), documents the guards and the stablecoin-gas payee funding requirement.render.yamlcarries the production recipient and keeps the payee key dashboard-only.Verified
tsc --noEmitclean; full boot matrix exercised locally:/,/llms.txt,/SKILL.md,/openapi.json✅eth_chainId→ 4217 on rpc.tempo.xyz; USDC.esymbol()/decimals()at0x20C0…8b50→USDC.e/6; matches the official tokenlist.Deliberately not in this PR (known follow-ups)
Store.memory()voucher state is still in-process — restart before settlement loses revenue (mppx ships redis/upstash stores +settlementSchedule)NETWORKenv var; no client↔server chain cross-checkCutover (after merge — dashboard, not this repo)
NETWORK=mainnet+ randomMPP_SECRET_KEY+ ownerTEMPO_RECIPIENT+TEE_ENDPOINT+ payee key (funded with a little USDC.e — Tempo gas is paid in stablecoin), then an explicit deploy; the service has never had a push-triggered deploy, so userender deploys create.