lume

14 — Zero-C: when (and in what sense) Lume builds Lume without C or assembly

A recurring, fair question: “From what point does Lume generate Lume without needing C or an external assembler — and why do the tools still contain any C at all?” This note answers it precisely, and describes the named CI gate (make zero-c, job “Zero-C steady state (no cc/as/ld)”) that proves the answer on every push.

The short answer

So “Lume generates Lume with no C and no assembly” is true from Stage 1 onward — the moment the seed-built tools rebuild the toolchain from Lume source. The three later stages, the fixpoint, and the lume CLI’s native builds never touch C or as/ld.

The staged bootstrap (where C lives, and where it does not)

scripts/bootstrap.src runs the GCC/Rust model in native Lume:

Stage Who runs Uses C? Uses as/ld?
0 — seed mint lume0 (C) mints lumec0, lasm0 yes (the seed, by design) no — lume0 writes ELF itself
1 — Lume rebuilds Lume lumec0 + lasm0 (Lume-built) no no — lasm writes ELF
2 — rebuild again lumec1 + lasm1 no no
3 — once more lumec2 + lasm2 no no
fixpoint assert stage 2 == stage 3, bit for bit — —

Comparing stage 2 to stage 3 (not stage 1) is deliberate and is what GCC does: stage 1 is produced along a seed-influenced path, so the fixpoint is the point from which the Lume-built tools reproduce themselves exactly. as/ld are replaced throughout the steady state by lasm (Lume); the cc/gcc compiler is replaced by lumec (Lume).

Why the C seed stays (and is not replaced by a committed binary)

bootstrap/lume0 is the reproducibility root, kept frozen exactly as GCC needs a system C compiler and Rust needs a prior rustc. Keeping it as auditable C source (rather than removing C by committing a Lume-built binary seed into the repository) is a deliberate security choice:

“Zero-C” therefore means zero C in the steady state, with a small, frozen, auditable C seed as the from-source root — not “no C anywhere in the repository.” That is the strongest honest claim, and it is the one the gate below enforces.

The gate: make zero-c — proven adversarially

A claim that the steady state is C-free is only worth the proof behind it, so the gate does not merely avoid calling C — it makes calling C impossible and shows the build still succeeds. scripts/zero_c.src implements the gate in Lume:

  1. Verifies the frozen seed hashes and mints lumec0/lasm0 (the one place C is allowed).
  2. Shadows cc, gcc, clang, c++, g++, as, and ld on PATH with copies of scripts/zero_c_blocker.src, a native Lume executable that fails loudly (exit 97) if invoked, and verifies every blocker is actually selected and returns 97.
  3. Runs stages 1-3 under that shadowed toolchain and asserts stage 2 == stage 3 (both the .s and the native binaries), then compiles and runs a sample program whose output must be 55.

If any C compiler or external as/ld had crept into the steady-state path, a native blocker would fire and the build would fail. Green means: stages 1-3 built Lume-with-Lume and assembled native ELF with lasm, and cc/gcc/clang/as/ld were never invoked.

Run it locally with make zero-c; in CI it is the job “Zero-C steady state (no cc/as/ld)”, alongside the self-host fixpoint gate (make bootstrap).

Frozen C seed policy & lasm.src self-validation (#205)

Now that the self-host loop is closed, bootstrap/lume0/*.c and bootstrap/lume0/*.h are strictly frozen:

  1. Cryptographic seed freeze (bootstrap/lume0/SEED_FREEZE.sha256): scripts/zero_c.src verifies the exact SHA-256 of every .c and .h file in bootstrap/lume0/ before minting stage 0. New language features, directives (such as .byte for P33 .rodata protection), and target formats must be implemented in Lume (compiler/stage1/lasm.src, compiler/stage1/lume/*.src), never by growing the C seed. The only permitted edits to bootstrap/lume0/ are critical security/UB fixes that update SEED_FREEZE.sha256 in an explicitly reviewed seed-maintenance commit.
  2. Retiring lume0 asm as an assembler oracle: In bootstrap/lume0/tests/run.sh, lasm.src is initially cross-checked against lume0 asm on the minimal bootstrap instruction subset (cmp -s lasm.elf c.elf). For all post-Stage-1 features (.byte, P33 protection, new target triples in docs/26-targets.md), lasm.src is self-validating: its output is verified by executing the resulting binary, checking its ELF/section invariants and contracts, and proving stage 2 == stage 3 bit-for-bit without consulting lume0 asm.