Every picture here is the real firmware running on the emulator with its virtual ATECC608, shown
at 2×. node tools/board/shots.mjs regenerates them all. They were made against the console’s test
harness (test-vectors/console_test.py), so the Safe is a stand-in (0x5a5a…) on Base Sepolia.
How to review from your phone: comment on any image in the pull request, or tick and edit the change requests at the bottom.
main_console.py). It can do nothing else:
firmware/manifest.json lists every file the image holds, and a test checks that nothing else
on the board can sign or change the chip.QUORUM_EXPECT in the board’s secrets.py names:
This is checked at start and again right before every signature. tools/fw pin writes it.
/sim calls go to the real service.log.txt) and a watchdog.![]() |
![]() |
| Home. X now opens Device instead of the chip map. The refused row says why in two words. | Device (X). Build, chip serial, slot and lock, key fingerprint, pin, the chip’s signature count, the service and whether its certificate was checked, poll periods, memory. Read-only. |
![]() |
![]() |
| A delegatecall to an unpinned contract: refused. Before, this was a red page you could sign with a 3 s hold. | Signed. The last line is the chip’s own count, which the chip raised itself. |
![]() |
![]() |
Signatures made while the console wasn’t looking (the chip’s count moved while the console was off). Any key clears the banner; log.txt keeps it. |
Not the pinned key. The chip, slot or owner doesn’t match QUORUM_EXPECT. It shows what it found next to what is pinned (serials by their last 8). Nothing signs. |
![]() |
![]() |
Not pinned. The production image with no QUORUM_EXPECT. tools/fw pin reads the chip, computes the owner on the laptop, compares it with the enrolled one, and writes the pin. |
No Safe. The production image never falls back to the laptop’s simulated Family Safe. |
![]() |
![]() |
Stopped. After 20 failed ticks in a row. error.log has the traceback. B restarts. |
Maintenance. Hold B at power-up. Plain text that draws even when nothing else does. |
The ceremony image (tools/fw push ceremony) opens on a banner that says it is not a signing
console. A starts PROVISION. PROVISIONING.md has the table byte by byte, the
reasoning, and the runbook.
![]() |
![]() |
| The ceremony image’s banner. | C1 pre-flight. Every step is worked out from the chip itself, so a reboot resumes. |
![]() |
![]() |
| C1’s checks. Address, revision (608A or 608B), both zones open, no slot locked, the factory table. The factory config is saved. | C2: quorum v1 written and read back, and the config CRC to compare on the laptop (tools/check_config --summary). |
![]() |
![]() |
| C3, permanent. Arm with B+Y for 3 s, then hold A for 3 s. The chip checks the CRC itself and refuses to lock a table one bit off. | C3 without ALLOW_LOCK: it says why and does nothing. |
![]() |
![]() |
| C4: the key, made inside the chip. GenKey on any other slot is refused. | C5: two test signatures verified on the Pico. The counter goes up by 2. |
![]() |
![]() |
| C6: slot 0 sealed. Same key, still signs. | C7: the owner address, in full, to compare on the laptop before anything is enrolled. |
![]() |
![]() |
| C9: the data zone locked, checked by its CRC. The OTP record reads back, slot 1 is unreadable, OTP is frozen, and it signs under the final rules. | C10: the summary. The one red line is the reminder to set ALLOW_* back to False. |
The steps not shown (C2, C4 to C10 before they run, C8’s result) are in shots/.
The board keeps an index of every chip it has seen, chips.json (firmware/chips.py). Every push
keeps it.
tools/fw chips lists the index.It is cosmetic. What gets signed is decided by the pin (QUORUM_EXPECT), read from the chip itself.
Chip 2 on a board pinned to chip 1 is named and drawn light, and refused all the same.
![]() |
![]() |
| The ceremony image with the fresh chip: chip 2, fresh, light. | A permanent step in chip 2’s colours. |
![]() |
![]() |
| The console on chip 2, light. | Device: chip 2, slot 0 locked, quorum v1. |
| Suite | Checks |
|---|---|
node test-vectors/run-console.mjs |
64: every refusal, pin mismatches, hold timing, the signature verified against the chip’s key, counter, request counts, production guards, crash state, watchdog cap |
node test-vectors/run-ceremony.mjs |
62: C1-C10 on a fresh 608A and 608B with a reboot after every step, the refusals, then the console signing with the sealed chip |
node test-vectors/manifest.mjs |
Each image is exactly its imports. In the console image only quorum.sign_it signs, and nothing can change the chip |
python3 tools/check_config |
Both tables, with every quorum v1 slot decoded independently from the datasheet’s bit tables |
tools/quorum e2e |
One approval on an Anvil fork of Base, executed onchain |