Architecture
One source tree builds the firmware for nine boards across two chip families. What makes that possible is not cleverness; it is a shape that is checked on every commit: a module may use the modules below it and nothing above, and the build fails when that stops being true.
Like the security page, this one describes a repository you cannot open yet. The check runs on every commit; letting you run it yourself is a step on the roadmap.
The order nothing may climb
Twenty-two modules in one order, the consumer first. A module includes its own headers and those of the modules below it; an include pointing upward fails the check, in the commit hook and again in continuous integration.
The remedy for an upward include is never an exception. It is to move the header down, or to invert the call through an interface the lower module declares, which is how the admin panel reaches the running application without the application knowing that a panel exists. There is a mechanism for pinning an exception, site by site. It is empty, and the point of it is that it is empty: no two modules in the firmware depend on each other.
Includes are spelled from the root of the source tree, so a relative path that climbs out of its own directory fails the same check. Inside a module the check goes further and fences groups of files off from each other, and it refuses a cycle between headers. The picture in the next section is generated from those same include lines, and the check fails while the copy of it kept in the repository is out of date.
The graph
The module graph is generated from the includes themselves rather than drawn by hand, which is what stops a diagram and the code it describes from disagreeing: the tool that draws it is the tool that enforces the order.
Assembly and control
The files that name every part of one board and drive it. They sit at the top because they reach down to everything; nothing reaches them.
- The board's wiring
boards/One file per device: which panel, which power controller, which buttons this board actually has. usesui/diag/hal/config/ - The bench partner
tools/A second board that plays the computer, so the radio and the admin page can be tested against something real. usesplatform/Not in every build - The state machine
main.cppBoot, then one loop: asleep, locked, open, typing, admin, and every move between them. usesdebug/states/web/ui/reset/net/ble/diag/vault/totp/i18n/crypto/hal/drivers/time/platform/ota/config/ - The developer's console
debug/A serial command line that can drive every screen. It is not compiled into a build you would run, and each build is checked for it. usesstates/web/ui/reset/net/ble/diag/vault/totp/i18n/crypto/hal/drivers/time/platform/config/Not in every build Tested on a PC
Screens and surfaces
Everything a person looks at or types into, on the device or in a browser.
- The screens
states/What each state of the device shows and what a press does there: the PIN dial, the vault, the settings tree, the first run. usesweb/ui/reset/net/ble/diag/vault/totp/i18n/crypto/hal/drivers/time/platform/config/Tested on a PC - The admin page
web/A page the device serves from its own Wi-Fi, for bulk edits and firmware updates. Off until you turn it on, and never on while the radio types. usesui/reset/net/ble/diag/vault/totp/i18n/crypto/hal/drivers/time/platform/config/Tested on a PC - The drawing library
ui/Every pixel: lists, dialogs, icons, brand marks. One set of parts that fits nine screens, from a 1-bit e-paper square to a color touchscreen. usesble/vault/brands/totp/i18n/crypto/hal/time/platform/config/Tested on a PC
Device services
What the device does once it is open: type, connect, erase, measure itself.
- The erase
reset/One place that erases everything (vault, pairings, settings), so every button and every page that wipes does exactly the same thing. usesnet/ble/vault/platform/Tested on a PC - The network errands
net/Joining a Wi-Fi long enough to set the clock, and remembering the name the admin page answers to. usesble/vault/crypto/time/platform/Tested on a PC - The keyboard it becomes
ble/The Bluetooth keyboard your computer sees. It only sends keystrokes: there is no path by which it could read what you type. usestime/platform/Tested on a PC - The instruments
diag/What the device measures about itself: battery, free memory, how long a frame took to draw. useshal/drivers/time/platform/Tested on a PC
Data and secrets
What the device holds and what locks it. Below every screen and every radio: the vault does not depend on the parts that draw it or send it.
- The vault
vault/Your credentials in flash, each one a sealed record of its own, and the only code that opens them. usesbrands/totp/crypto/hal/time/platform/config/Tested on a PC - The logo lookup
brands/Turning a web address into the logo you recognize on the row, from a table built into the firmware. Nothing is fetched. usesplatform/config/Tested on a PC - The six digits
totp/The two-factor code for an account, and the clock rule that refuses to show one when the time cannot be trusted. usestime/platform/config/Tested on a PC - The words
i18n/Every sentence the device can show, in every language it speaks, in one table. usesplatform/Tested on a PC - The lock itself
crypto/Turning your PIN into a key, and the key into sealed bytes. The arithmetic, and nothing else: it knows nothing about screens or records. usesplatform/Tested on a PC
Hardware and foundation
The chips, and the single door to the chip's own services. Nine different boards meet the rest of the firmware here.
- The translator
hal/One vocabulary for nine boards (draw, read a button, measure the battery), whatever the chip underneath turns out to be. usesdrivers/time/platform/config/Tested on a PC - The chip drivers
drivers/One driver per real chip: screen, power controller, clock, motion sensor, keyboard. Written here rather than borrowed. usestime/platform/config/Tested on a PC - The clock and its trust
time/What time the device thinks it is, where that came from, and whether that is good enough to show a two-factor code. usesplatform/config/Tested on a PC - The foundation
platform/The single door to the chip's own services (tasks, timing, logging, storage, sleep), so nothing above ever calls them directly. usesota/Tested on a PC - The update rule
ota/Whether an offered firmware may be installed. Arithmetic on version numbers; it depends on nothing at all. uses no other module Tested on a PC - What this board is
config/The facts about the board, fixed when the firmware is built: screen size, flash size, which parts exist at all. uses no other module
Point at a block, or tap it, to light up the blocks it depends on. They are all below it.
ble/, which depend on each other by design and are pinned by name. Two cores, and what is not allowed on each
The chip has two cores, and the division between them is a rule rather than a tendency.
Core 1
Takes the PIN's last digit
Draws the progress, and never waits
Draws the vault
Core 0
Idle
Derives the key: PBKDF2-SHA256, 175,000 iterations, about 1.5 s
Opens the vault's index
Clock
80 MHz
240 MHz
80 MHz
-
Core 1: what you look at
The main loop, the state machine, the screen and the buttons. It never blocks on a radio or on storage, because every millisecond it spends waiting is a millisecond the device feels slow in your hand.
-
Core 0: what you wait for
The Bluetooth host, Wi-Fi and the web server, the filesystem, and the cryptography. Deriving the key takes about a second and a half: it runs here, with a progress indicator drawn on the other core, and at the fastest clock the chip has, whereas every screen runs at the slowest.
A queue or a lock that either of two tasks could reach first is built before the task that consumes it exists, or inside a call that happens once. Checking whether it is null and creating it if so is the bug that looks correct: both cores pass that check, both build one, and one of them is waited on by nobody.
One tree, nine boards
A board is a configuration, not a copy of the firmware.
Each board has a directory holding its pin map, its panel, its power controller, its partition layout and its own figures, the sleep estimate the measurements page prints among them. The firmware builds on ESP-IDF 6.0.3 with no Arduino core in it, as C++26, with every compiler warning treated as an error. A guard reads all nine boards and fails on an inconsistency between them: thirty-odd invariants, including that every build carries exactly one key-derivation work factor, and that the serial console cannot be compiled into a production image.
Every board has up to four builds: an open development one, a debug one that never ships, an open production one, and a hardened one that irreversibly burns one-time fuses on its first boot. Three boards have no hardened build, because 4 MB of flash does not hold two update slots and a signed bootloader.