Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

How hard the endpoint is to attack

In short

  • The endpoint is built in layers: check only what it computed itself, accept only one exact message format, contain the proof checker, and run on a system with almost nothing else on it.
  • The catalogue has 49 scenarios: 37 stopped, 2 allowed on purpose, 8 that still work, are only partly stopped or are untested, and 2 out of scope. Some were shown on the Pi, some only in tests, and some are only proposed; the counts describe coverage, not a security score.
  • Code reviews found five real bugs in the code that holds answers back until their turn; all fixed, each now covered by a test that fails on the old code.

The layers

1. Check only what the endpoint computed itself. The endpoint never trusts the server’s account of the question or the model. It encodes the question itself, pins the approved model’s files by hash, fixes the proof length, and reads the output only from the proven values. See How the endpoint works.

2. Accept one exact message format. Questions, forwarded questions and replies have one fixed size and layout for the model in use. The server’s answer is a fixed part plus the proof, which may be up to 64 KiB on the wire and must then be exactly the pinned length. Anything else is rejected: oversized or truncated messages, wrong types, numbers that aren’t finite. A fuzz test throws 10,500 random and mutated messages at each parser.

3. Contain the proof checker. The checker is a separate program with hard limits: 256 MiB of memory (enforced on Linux, which the Pi runs), CPU time up to the deadline, no file growth, 64 open files, no crash dumps. It is killed if it runs past its deadline or prints too much. A checker that hits a limit counts as a failed check, never as a pass. In the Rust endpoint on Linux, the checker also runs under a system-call allow-list once its limits are set: it can’t open files or network connections, run programs, start processes or map executable memory, so a bug in the proof code can’t reach anything outside it (it could still print a wrong verdict).

4. Say nothing the server chose. Users only ever see the proven output or a fixed error (unavailable, or bad_request for a well-formed question the model can’t answer); a malformed message just closes the connection. The endpoint’s log uses a fixed vocabulary, never text from the server; it still records a counter, a hash of the question and the time taken, and which events happen and when still follow the server.

5. Run on a minimal system (v1). The standard Pi system has ssh and other services listening. The v1 image is built for the endpoint alone: one static Rust binary running as an unprivileged user, a read-only system, one open port behind a firewall built into the kernel (the endpoint won’t start without its rules), no ssh and no scheduled jobs. The 2 Nov rebuild also locks the kernel down (no kernel modules on the image and module loading switched off at boot, kernel lockdown, no raw memory, debugger or tracing interfaces, nothing for IPv6, radios, sound or USB) and removes every login prompt; its check on the Pi is pending.

6. Accept only signed changes. The model files and the release policy come from a signed update: the endpoint refuses rollbacks, replays, updates for another device and unknown or revoked keys (catalogue rows K36-K48). Still planned (v2): secure boot, so the board starts only a system we signed, and a counter in a TPM so a whole older copy of the store can’t be restored (K43).

Catalogued scenarios

OutcomeScenariosExamples
Stopped37Edited answers, a proof for a different question, a proof from another model, padded proofs, garbage or oversized messages, a taken-over checker reaching files or the network, answering twice or late, malicious proofs that try to exhaust the checker, using a disconnect to stall the endpoint, rolled-back, replayed or substituted updates, a changed or old release policy, updates for another device or from an unlisted key, overlapping or live installs, a key revocation that didn’t stick, a hand-edited state
Allowed on purpose2The same question gets the same answer again (the server has no choice there); an endpoint set to stop after failures can be stopped by the server
Still works, partly stopped or untested8Timing (much reduced), failing on purpose, never answering, keeping the endpoint busy, loading the endpoint to delay releases, other services on the standard system, probing the endpoint’s rules, restoring a whole older copy of the update store
Out of scope2Learning the model from many correct answers; physical attacks

Every scenario has a row in the Attack catalogue with its result and its evidence, or says that there is none yet.

Bugs found by review

Independent reviews of the code (run with AI tools) found five real bugs, all in the code that holds answers back until it is their turn:

  • an answer that was already checked could still leave after the endpoint had decided to stop answering;
  • in arrival order, under a stalled event loop, a checked answer could leave after its own deadline;
  • answers waiting their turn didn’t count towards the endpoint’s capacity limit, so the queue could grow without bound;
  • with fixed-time release and the spin option, a later answer could overtake an earlier one whose deadline had nearly passed, so the server could choose the order of two failures again;
  • with spin, a disconnect or halt that decided many answers at once chained one busy-wait per answer, stalling the endpoint for tens of milliseconds.

Each now has a test that fails on the old code (catalogue rows K30, K31, K33 and K35).

What isn’t hardened yet

Both sides still share one cable, so nothing physically forces the server’s traffic through the endpoint; there is no secure boot; a whole older copy of the update store is accepted (K43); a new update needs a new image or an offline step; the verifier’s own verdict is trusted; and the Linux kernel, though trimmed, is still large. See Limitations and open questions.