Germ, a tiny new Scheme interpreter, aims to shrink Guix's bootstrap chain

This year's FOSSY, the Free and Open Source Software Yearly conference, moved to the University of British Columbia in Vancouver, Canada, after three previous editions in Portland, Oregon. In the "Toolchains and Other Development Tools" track, developer Timothy Sample gave a talk on bootstrappable builds, a topic he called somewhat less well known than its cousin, reproducible builds, which LWN had covered just over two years earlier. He opened by polling the room and found that most attendees already recognized the term, with roughly half familiar with it in more depth. A bootstrappable build starts from a tiny program that can build a slightly larger one, which builds a larger one still, and so on, until an entire modern Linux userspace is built up from a small seed with a fully known origin, unlike a typical Linux userspace today.

Sample said he started down this path almost ten years before the talk, when he began using GNU Guix, a "functional package manager" similar to, and inspired by, Nix; back then, he said with a laugh, using Guix meant contributing to it. In Guix and Nix, every program in the system sits in a "derivation graph" describing what is needed to build it and, recursively, what is needed to build each of those inputs in turn, producing what Sample called hundreds and hundreds of nodes. He illustrated the resulting chicken-and-egg problem with a Python program: Python needs a C compiler, that C compiler was itself written in some other language that needs its own compiler, and so on, until, as he put it, someone has to ask "who compiles my compiler's compiler compiler and where does it stop." A distribution like Debian stops that chain at a C compiler binary someone has uploaded to its repositories. Guix used to stop at a 250MB statically linked blob of GNU userspace programs, reproducible but of unclear origin; today its seed is only about 256 bytes, a program called hex0.

Reproducible builds let a user rebuild a binary from its source and check that the result is bit-for-bit identical to what they were given, which catches a builder who is lying or mistaken about where a binary came from. Bootstrappable builds guard against a different failure: the kind of compiler backdoor Ken Thompson described in his Turing Award lecture, "Reflections on Trusting Trust," in which malicious code hides inside a self-hosting compiler's binary without ever appearing in that compiler's own source code. Sample said a colleague had recently pointed him to a paper describing an actual attack of this kind: researchers inserted a backdoor into the strip program on NixOS, a tool that runs on nearly every binary built on the system, and were able, in his words, to "backdoor basically every single program on the system in a way that's completely invisible from source-code analysis." That, Sample said, is exactly the class of attack bootstrappable builds are meant to close off; beyond security, he added, there is a software-freedom case too, since being able to inspect code is one thing and knowing that the running program actually corresponds to that code is another.

For tools that only support a self-hosted build, Sample described two techniques used in the bootstrappable-builds community. One is proactive: keep and maintain a non-self-hosted implementation alongside the self-hosted one, the way GNU Guile keeps a C implementation of itself and GNU Make ships a fallback shell script for systems with no make available. The other is retroactive, an "archaeological dig" through a project's version history to find its earliest non-self-hosted release and then rebuild forward, version by version, until reaching the modern tool, a slow process that can mean stepping through a dozen or more intermediate versions. A third option is a purpose-built bespoke tool, which Sample illustrated with Rust: Guix currently bootstraps Rust using the C++-based mrustc, which builds Rust version 1.54 or 1.56, after which nearly every version up to today's 1.97 has to be compiled in sequence, a chain slow enough that one attendee said it took three days on an Arm laptop (mrustc can now build Rust 1.90, though that has not yet been integrated into Guix). Guix's own chain starts from hex0, a seed of about 256 bytes, and moves through the hex1 and hex2 hexadecimal converters, then M0, then the C-like M2-Planet, at which point everything switches to GNU Mes, a Scheme interpreter written in M2-Planet's C dialect that includes its own C library and a C compiler written in Scheme; together those can build the Tiny C Compiler and, from there, modern development tools. A related project, live-bootstrap, follows a similar path but moves faster than Guix, has explored bootstrapping the kernel itself using the Fiwix kernel, and documents the whole route as a 182-step process to reach a base GNU/Linux system, not counting further tools like Rust or Go.

Sample's own contribution is a different approach called Germ, first introduced in a blog post on his site: rather than follow Guix's existing chain, which round-trips from C to Scheme and back to C before landing in the Scheme-based Guix, Germ starts with a Scheme interpreter from the beginning, on the reasoning that "a primitive Lisp interpreter is not much more complicated than a primitive assembler." Germ is about 2.25KB, short of the 2KB he had aimed for, and is written in assembly rather than a higher-level language; it runs "almost-Scheme," just enough to execute an assembler written in Scheme, which in turn builds a fuller second-stage interpreter with contiguous bytes and vectors, I/O and kernel access, and near-compatibility with unmodified Guix build scripts. Sample also maintains a parallel, hand-written assembly version of Germ's code alongside its Scheme-based version, since a real assembler provides debugging symbols the Scheme-based one does not. He demonstrated Germ live rather than with slides alone: his presentation itself ran on Germ, through an SDL interface he built with a "draw pixel" function and a font pulled from the original Symbolics Lisp machine, correctly identified by an audience member.

Performance is Germ's biggest current problem, by Sample's own account. Writing an interpreter in assembly, the way the earliest Lisp interpreters were built in the 1950s, rules out most modern implementation techniques. Germ is technically faster than Mes on a microbenchmark of raw function calls, but slower on real programs, since most of what it runs, including loops, executes in interpreted Scheme rather than assembly. Concretely, Mes takes about 100 seconds to compile itself on Sample's desktop, or about 60 seconds with an experimental bytecode compiler, while compiling Mes on Germ, with optimizations he is currently testing, takes around 140 to 150 seconds on the same machine; since Sample already considers Mes "unbearably slow," trailing it further is a real problem, not a minor one. Germ is also less portable, running only on x86_64 so far, while Mes already supports Arm and RISC-V. He named a subtler obstacle too: "people just hate parens," a reaction he says he understands, having felt the same way about Lisp and Scheme himself before encountering Guix.

Looking ahead, Sample said he would like to integrate Germ into Guix as a replacement for %bootstrap-guile, the statically linked Guile binary Guix currently depends on and which he called "absolutely cheating"; replacing it with Mes instead would also be a step forward, he said. He also wants to keep improving Germ's performance, is weighing whether to write it a proper compiler backend for C, and has started, without yet finishing, a RISC-V port. In the Q&A that followed, Keith Packard asked how much of Scheme Germ actually implements, floating a comparison to the older R5RS standard; Sample described it instead as a Guile Scheme without extras like the Guile Object Oriented Programming System (GOOPS), effectively an R7RS Scheme, with deliberate omissions such as floating-point numbers, which he said he did not need. Mark Wielaard asked how many of the chain's 182 steps Germ removes; Sample said that in his "dreaming mind" Germ would become a wedge other developers use to build further Scheme-based shortcuts, cutting long sub-chains such as Perl and Autoconf, and he accepted Wielaard's own estimate that more than 80 steps still remain even with Germ's help. Wielaard also pointed out that someone could sabotage one specific step, step 73, and it would likely go unnoticed under the current chain, a known flaw Sample agreed with, while arguing that shrinking the step count still improves verifiability over time. An attendee suggested the seed could be made smaller than 2.25KB by using hex0 to load Germ's first stage; Sample agreed it was possible but said he considers that "a bit like cheating" and would rather "actually get results" than "play games." Asked whether Germ has a read-eval-print loop, Sample confirmed it does, having used the REPL throughout the talk to advance his own slides and demonstrate error backtraces; despite cutting corners such as floating-point support, he kept features he called necessary "creature comforts," including delimited continuations. He closed by noting that Germ has "the fanciest macros," including a working syntax-case form he had to implement himself, since, he pointed out, no bootstrappable syntax-case already exists, for the simple reason that syntax-case implementations are themselves normally written using syntax-case.

Key facts

  • At FOSSY 2026, held this year at the University of British Columbia in Vancouver after three editions in Portland, Oregon, Timothy Sample gave a talk on bootstrappable builds and introduced his own project, Germ, an assembly-written Scheme interpreter of about 2.25KB.
  • Guix's bootstrap seed has shrunk from an original 250MB statically linked blob of GNU userspace programs to about 256 bytes today, the hex0 program; from there Guix's chain runs through hex1, hex2, M0, M2-Planet, GNU Mes and its MesCC compiler, and the Tiny C Compiler before reaching modern development tools, a route the related live-bootstrap project documents as 182 steps.
  • Bootstrappable builds guard against Ken Thompson's "Trusting Trust" compiler-backdoor attack; Sample cited a real instance, a paper describing researchers backdooring the strip program on NixOS, a tool run on nearly every binary built on the system, in a way invisible to source-code review.
  • Germ aims to skip the current chain's round trip from C to Scheme and back by starting with a Scheme interpreter directly; Sample demonstrated it live, running his own conference slides on Germ through an SDL interface using a font from the original Symbolics Lisp machine.
  • Germ remains unproven on real work: compiling Mes on Germ currently takes about 140 to 150 seconds against about 100 seconds (60 with an experimental bytecode compiler) for Mes to compile itself, Germ runs only on x86_64 versus Mes's support for Arm and RISC-V, and Mark Wielaard estimated more than 80 of the chain's 182 steps still remain, with undetected sabotage still possible at one specific step, step 73.

Why it matters

Bootstrappable builds close a specific gap that reproducible builds cannot close on their own. A reproducible build lets a user rebuild a binary from source and confirm it is bit-for-bit identical to the one they were given, which catches a builder who is lying or mistaken about where a binary came from. It does nothing, however, against Ken Thompson's classic "Trusting Trust" attack: a backdoor baked into a self-hosting compiler's binary that never appears in the compiler's own source code and silently reproduces itself every time that compiler builds a new version of itself. Sample made the threat concrete rather than hypothetical: a colleague had pointed him to a recent paper describing an actual attack of this kind, in which researchers backdoored the strip program on NixOS, a tool that runs on nearly every binary built on the system, making it possible to backdoor essentially every program on a machine in a way invisible to any review of the source code. A bootstrappable build closes that gap by tracing every tool back to a small seed with a fully known origin, so nothing in the chain can hide behind an unauditable pre-built binary. Sample's own talk doubled as a case study for that goal: he introduced Germ, a new interpreter he built from scratch specifically to shorten and simplify the chain Guix currently needs to get there.

Who it affects

The talk speaks most directly to the existing bootstrappable-builds community around Guix and the related live-bootstrap project, and to anyone maintaining a self-hosting language toolchain that faces the same chicken-and-egg problem. Rust is the example Sample gave: it currently bootstraps through the C++-based mrustc, which builds Rust version 1.54 or 1.56, after which nearly every version up to today's 1.97 has to be compiled in sequence, a chain slow enough that one attendee said it took three days on an Arm laptop. Two named attendees pressed Sample on specifics during the Q&A: Keith Packard questioned how much of Scheme Germ actually implements, and Mark Wielaard, who estimated that more than 80 of the chain's 182 bootstrap steps still remain even with Germ's help, pointed out that sabotage at one particular step, step 73, would likely go unnoticed under the current setup. More broadly, the talk is aimed at anyone who treats software supply-chain integrity as a real concern, since the NixOS strip backdoor paper Sample cited shows the attack class is not theoretical.

How to use it

Germ is not something a Guix user can drop in today: at the time of the talk it exists as Sample's own project, first introduced in a blog post on his site and demonstrated live rather than shipped as an integrated replacement; the source does not say whether Germ's code is publicly released or under what license. The practical path into bootstrappable Guix builds still runs through the existing chain: hex0 builds hex1 and hex2, which build M0, which builds M2-Planet, which is used to build GNU Mes and its C compiler MesCC, which build the Tiny C Compiler, from which modern development tools follow. The related live-bootstrap project documents that whole route as a 182-step recipe for a base GNU/Linux system, before Rust or Go even enter the picture. Sample's own next step for Germ is to integrate it into Guix as a replacement for %bootstrap-guile, the statically linked Guile binary Guix currently depends on and which he called "absolutely cheating"; handing that role to Mes instead would also be a step forward, he said. He also plans to keep working on Germ's performance, is weighing whether to write it a proper compiler backend for C, and has started, but not finished, a RISC-V port to go alongside its current x86_64-only support.

How solid is it

This account rests on a single LWN conference report, byline Jake Edge, dated August 17, 2026, rather than a paper, a project page, or Germ's own documentation. Its strength is that it records a live talk with real-time pushback: two named attendees, Keith Packard and Mark Wielaard, questioned Sample directly during the Q&A, and his answers, including an admitted, known weakness in the current bootstrap chain, are captured alongside the pitch rather than filtered out of it. The specifics throughout are concrete and internally consistent: size figures for the bootstrap seed and Germ itself (about 256 bytes, a 250MB original blob, about 2.25KB), exact version numbers for the Rust bootstrap chain (1.54 or 1.56 up to today's 1.97, with mrustc now able to reach 1.90), and a named external paper backing the NixOS backdoor claim. What the article does not supply is Sample's professional affiliation or employer beyond his work on GNU Mes, the exact date of the talk within the conference, the authors of the NixOS backdoor paper, or whether Germ's source is publicly available and under what license.

Risks and caveats

Germ is an early, personal project, not a finished replacement for anything in Guix yet: it was demoed rather than integrated, and the article gives no timeline for finishing its RISC-V port or for replacing the current statically linked Guile with Germ or Mes. By Sample's own numbers it is currently a step backward on real work: Mes takes about 100 seconds to compile itself (about 60 seconds with an experimental bytecode compiler), against roughly 140 to 150 seconds to compile Mes on Germ with the optimizations he is testing, because most of what Germ runs executes in slow interpreted Scheme. He already calls Mes "unbearably slow," so trailing it further is a real problem, not a rounding error. Germ currently runs only on x86_64, while Mes already supports Arm and RISC-V. And even if Germ succeeds, the underlying security promise stays partial: Mark Wielaard estimated more than 80 of the chain's 182 steps would still remain, and he flagged one of them, step 73, as a point where sabotage would likely go unnoticed today. Separately, and unrelated to Germ specifically, Sample noted that bootstrappable builds still have no answer for the kernel itself: Guix's whole chain assumes a kernel already exists and stops there.

“They were able to backdoor basically every single program on the system in a way that's completely invisible from source-code analysis.”

— Timothy Sample, on a real backdoor researchers inserted into NixOS's strip program