Files
roro9stack/site/content/dev/decisions/0009-the-device-trusts-the-isrg-roots.md
T
twislaandClaude Sonnet 5.5 3b4100dc6a
CI / build (pull_request) Successful in 8m38s
Site / build (pull_request) Successful in 9s
Site: the developer docs (phase 4), with the Debug Builds and the Debug Console first
/dev/ has Debug Builds and the Debug Console (builds and the token, the
console and its protocol, files and screenshots, driving the UI, crashes and
Safe Mode, the command reference), Build, test and release (including how an
update works), the architecture decisions and the milestone plans.

Generated from the repository by site/tools/gen_dev_docs.py: the ADRs, the
milestones, the README's sections, and the command reference, read from the
firmware's own `help` text. The pages are committed (Zola cannot read outside
its folder); the Site workflow checks they are current, and now also runs
when src/main.cpp changes. M0, M1 and CONTEXT.md are not published.
README: the gnss commands that the table lacked.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-06 21:25:33 +02:00

2.1 KiB

+++ title = "The device trusts the two ISRG roots for what it fetches" description = "The firmware talks to the project's Gitea over HTTPS (issue #6, and #4 after it). A TLS client has to decide whose certificates it believes. Three ways were possible:" weight = 9

[extra] docs = true source = "docs/adr/0009-the-device-trusts-the-isrg-roots.md" tag = "ADR 0009" +++ The firmware talks to the project's Gitea over HTTPS (issue #6, and #4 after it). A TLS client has to decide whose certificates it believes. Three ways were possible:

  • The framework's bundle, about 130 certificate authorities (about 60 KB of flash). Any of them could vouch for git.twis.la.
  • Pin the server's certificate, as the Gemini App does for capsules. The server's certificate is replaced every few months, so a pin would ask the question again at every renewal.
  • Carry the roots the server's chain ends in: ISRG Root X1 (RSA 4096) and ISRG Root X2 (ECDSA P-384), the Let's Encrypt roots, about 2.7 KB of flash (src/platform/ca_roots.h).

We chose the third. The chain and the name are checked by mbedTLS during the handshake. It trusts one organisation's two roots, valid until 2035 and 2040, and a renewal changes nothing.

What it costs

  • If the server moves to another CA, the device can no longer reach it, and the next firmware, carrying that CA's root, has to come from the PC or the SD card. Both still work; they don't use TLS.
  • The roots are public data checked against the published fingerprints (listed in the file), refreshed by hand if Let's Encrypt ever changes them.

What it doesn't change

The Update File's own signature (ADR 0003) is what decides what gets installed. A hijacked connection could hide a release, or serve an older signed one, but never make the device install firmware that isn't ours. The TLS check matters more for #4, where a token will travel over it.

Measured while building it

A TLS connection to this server peaks at about 52 KB of heap, the same whether the certificate is checked or not, so skipping the check would have saved nothing. The cost is the connection itself (record buffers and handshake), not the trust decision.