This is the tracker for getting the console’s approvals from the laptop to real chains. Each rung runs the same firmware and the same approval: someone proposes, the console shows it, you hold A, and the Safe executes. Only the Safe and the money behind it change from rung to rung. Tick boxes as things land, and link the evidence (a buildlog entry, an explorer link).
How a chip key owns a Safe, on every rung:
firmware/webauthn.py, pinned in quorum_cfg.py "signer").| # | Rung | Where | Who signs | Status |
|---|---|---|---|---|
| 0 | Simulated | tools/quorum serve, no chain |
Alice and Bob (simulated), the real Pico | ✅ 2026-09-24, the real Pico (buildlog) |
| 1 | Local fork | tools/quorum up: Safe 1.4.1 on an Anvil fork of Base, chain 31337 |
Alice and Bob, the chip | ✅ emulator (tools/quorum e2e) · ✅ real Pico, 2026-09-25 (buildlog) |
| 2 | Base Sepolia | testnet: the Safe web app and api.safe.global |
your browser wallet, the chip | ✅ rehearsal with the emulator · ✅ real Pico, end to end over HTTPS, 2026-09-25 (buildlog) |
| 3 | Base | a few dollars | your browser wallet, the chip | ☐ |
| 4 | Ethereum mainnet | a few dollars (base fee about 0.05 gwei in Sept 2026) | your browser wallet, the chip | ☐ |
Everything is tools/quorum. tools/quorum doctor checks what’s installed.
| Command | What |
|---|---|
serve |
the simulated Family Safe (rung 0) |
up [--manual-exec] |
anvil forking Base, plus the service on a real Safe (rung 1). --manual-exec makes it wait for live exec, as Safe’s real service does |
e2e |
rung 1 end to end with the emulator’s chip, checked onchain |
live key [--from URL \| --usb \| --emu] |
the console’s P-256 key and its owner address, kept in live.json (public values only) |
live enroll --chain C |
deploy the key’s signer proxy (about 110k gas) |
live status --chain C --safe 0x… |
the Safe and its queue, every signature checked here |
live rehearse --chain C |
the emulator’s console, with a throwaway key, signs on the real service: the spike before the Pico |
live exec --chain C NONCE |
execute, with the signatures laid out right (see Known issues) |
live pico --chain C[,C…] |
point the board’s console at the Safe recorded for each chain (QUORUM_SAFES); several chains are watched together (guide/NETWORKS.md) |
C is local, basesep, base or eth.
How live sends. Writes go through your browser wallet, the one you co-sign with in the Safe
web app: live opens a one-page form on 127.0.0.1 and the wallet shows the transaction and asks.
--account NAME sends from a Foundry keystore instead, and asks you to type yes.
tools/mock-tx-service/live.json (gitignored).isValidSignature, and the transaction executes (e2e, about 110k gas with the precompile)live enroll, live rehearse, and live exec through the wallet pagetools/quorum up. It prints the QUORUM_URL line.secrets.py, then tools/emu ship quorum.tools/quorum demo, then approve #0 on the Pico. The Safe executes on the fork.Before you start:
SAFE_API_KEY= in the repo’s .env, which is gitignored (copy .env.example;
tools/quorum doctor says whether it’s set without showing it). Paste it in with your editor,
not on the command line, so it doesn’t land in your shell history.QUORUM_API_KEY in the board’s own secrets.py, set at the switch to this rung.
The board’s copy isn’t the same file as firmware/secrets.py, so add the line; don’t copy the
laptop’s file over it.Rehearsal: the emulator’s throwaway key. No hardware, and it settles the biggest unknown, whether
api.safe.global takes the console’s confirmation.
tools/quorum live key --emutools/quorum live enroll --chain baseseplive key printedtools/quorum live status --chain basesep --safe 0x…. Then send the Safe 0.001 ETH.tools/quorum live rehearse --chain basesep: the service holds the emulator’s confirmation.tools/quorum live exec --chain basesep <nonce>, sent from your wallet. Record the explorer link.The real Pico (with you at the board). Its own key needs its own Safe, or it has to be added to the rehearsal Safe as an owner.
tools/quorum live key --usb. Check the address against the Pico’s owners page (B, This key).live enroll, then a Safe with your wallet and the Pico’s signer.secrets.py:
QUORUM_URL = "https://api.safe.global/tx-service/basesep"QUORUM_SAFE = {…}, QUORUM_API_KEY, QUORUM_POLL_S = 20tools/emu ship quorum.live exec. Record the link in the buildlog.dataDecoded, so lower the limit if it doesn’t fit.Same steps as rung 2 with --chain base and QUORUM_URL = ".../tx-service/base". The plan
(2026-09-25):
tools/quorum live enroll --chain base for the sealed slot 0 key, owner
0x2843…5E53. The deploy is paid from MetaMask, about $0.002:
tx.0x61D2…637F, the Pico 0x2843…5E53, and his other wallet 0x1289…f2DA0xDebDE58b3ed0867d19285f6C8B0d81AdBC326Da9.
Its owners are MetaMask, the Pico and 0xb78e…AdBD8 (an EOA, not the 0x1289… in the plan;
confirmed Jason’s on 2026-10-06).live status --chain base --safe 0x…, plus the version, owners, threshold,
modules and guard read onchain, and the singleton and handler matched against safe-deployments.tools/quorum live pico --chain base, or --chain basesep,base
to keep watching the Sepolia Safe too (QUORUM_SAFES; each chain’s service comes from
quorum_cfg.py, so QUORUM_URL goes). The same API key.docs/app/, New transaction).live exec --chain base.Costs. About $5 of ETH on Base in your wallet is plenty. Each of these is a fraction of a cent: signer 110k gas, a Safe 310k, an execution 110k.
Before any real money, on the board (and the chip: LOCKING.md):
tools/fw push console. It boots straight into the console, and
nothing else on the board can sign or touch the chip. tools/fw verify should be all green,
and the build id shows on Device (X). Build 20464b0, 2026-09-26.tools/fw backup, copied somewhere outside the repo (it holds secrets.py).
Taken 2026-09-26 (backups/025997e61befcde3/); the copy outside the repo is still to do.tools/fw pin: the chip’s serial, slot 0 locked, owner 0x2843…5E53. The console
signs nothing else.verified. On the board, the compiled image from a mount
passed 8 of 8 verified polls, and badssl.com’s four bad certificates were refused on the
certificate itself (buildlog 2026-09-26).log.txt says so.
Interrupts off for 12 s: it reset, and the console came back by itself.ENABLE_NETWORK_CONSOLE = False. The network console is a passwordless REPL, and it can sign
(the production image ignores it anyway).ALLOW_GENKEY = False and ALLOW_LOCK = False (ignored by the production image too).Milestone:
Same steps with --chain eth. The console can watch it alongside Base: live pico --chain base,eth.
api.safe.global/tx-service/eth are all
there.quorum_cfg.py lists are per chain now,
with Ethereum’s USDC, and MultiSendCallOnly 1.4.1/1.3.0 and Permit2 under "*". All of them
have code on mainnet. node tools/vectors/webauthn.mjs --chain eth passes every vector:
getSigner gives the same owners as on Base Sepolia, isValidSignatureForSigner accepts them,
and the precompile at 0x100 is live. Nothing was sent.live enroll --chain eth, then a Safe on Ethereum (real gas: say yes first).cast base-fee first: L1 gas moves more
than Base’s.The same ladder for the OPTIGA Trust M (TRUSTM.md). The shareable docs are in mainnet/.
0x47c0998C6a7A1955794071b9f4058e7814178f35.0x5617…DC41, via swapOwner.mainnet/attest/attest-0a091b5c000b0062006c.json (2026-10-01).
tools/quorum verify --chain basesep,base,eth passes: getSigner agrees on all three chains.verify.live enroll --chain eth, the 3-of-5 Safe, one
approval (rung 4’s checklist).tools/quorum live exec.eth_call to it. live enroll does the deploy.QUORUM_POLL_S and QUORUM_IDLE_POLL_S override these.oeth), if we want it later.firmware/webauthn.py.swapOwner. Refuse them on the Pico instead, or ask for the 4-hex verify code?