Fork channel

Create a new channel as a copy of main.

Rename channel

Rename main to:

Delete channel

Delete main? This cannot be undone.

mode name
-rw-r--r-- README.md
-rw-r--r-- crates.lock
-rw-r--r-- elpe.SHA256SUMS
-rw-r--r-- guest-stage0.sh
-rw-r--r-- sq.lock
-rw-r--r-- toolchain.lock
-rw-r--r-- vm.lock
README

Nix-free seed (stage0)

Goal: produce the first elpe binary (Rust backend elpe-server) without Nix, from a content-hash-pinned toolchain. After stage0, elpe rebuilds itself (build.ml → Rust.rust, toolchain from Ubuntu, crates pinned by SHA256 in Cargo.lock). Full design + inventory: ../../../elpe.roadmap/project_nixfree_seed.md.

The one insight

Elpe already compiles its own Rust backend without Nix (build.ml, elpe/lib/rust/rust.ml). rustc/cargo come from Ubuntu (rust.ml:661,663), crates from crates.io pinned by SHA256 (rust.ml:8-17). The ONLY thing Nix uniquely produces is the first elpe binary needed to run build.ml — a chicken-and-egg. This directory pins the stage0 toolchain by hash so that first binary can be built without Nix.

Trust roots (all content hashes)

FilePinsStatus
toolchain.lockrustc/cargo/gcc/clang/libc6-dev/protoc (Ubuntu .deb, SHA256)filled + verified (resolute 26.04 LTS, rustc-1.93; all 6 .debs fetched & hash-checked)
../../src/ubuntu-archive-keyring.ascUbuntu archive PGP signing key — the InRelease trust root, embedded in the binary (src/pgp.rs)pinned (fingerprint F6ECB376…, replaces the former sq binary; sq.lock now obsolete)
crates.lock323 crate SHA256 — pointer to Cargo.lock (source of truth)derivable, run extract-hashes.sh
elpe.SHA256SUMSpre-built seed elpe binary (option 1)TODO — after reference build

Usage

Two ways to run stage0. Both need a real build (never in an agent fork).

test-stage0-vm.sh boots a pinned, pristine Ubuntu in QEMU and runs the whole stage0 there, so the “clean box” milestone is a repeatable test, not a manual procedure. It also gives the daemon the root/namespaces it needs (no host sudo — unblocks §9.9).

# On any KVM-capable Linux host with qemu + cloud-image-utils + pijul:
./test-stage0-vm.sh

What it does: fetch+verify the pinned cloud image (vm.lock, SHA256) → pijul clone the repo into an empty dir → boot QEMU headless with that clone mounted read-only over 9p and a writable results share → cloud-init runs guest-stage0.sh (provisions a cc + OCaml/dune/opam from the same signed resolute suite, then calls bootstrap-seed.sh) → the host reads back RESULT/rc/console.log and asserts success. The pinned base image is booted with -snapshot, so it is never mutated.

B. Manual — directly on a clean Ubuntu box

./extract-hashes.sh            # derive crates.lock from Cargo.lock (no build)
./bootstrap-seed.sh            # fetch+verify pinned toolchain, build stage0, close loop

toolchain.lock is already filled & verified against Ubuntu resolute 26.04 LTS (PGP InRelease → Packages.xz → .deb sha256), matching rust.ml (rust_version=1.93, ~release:"resolute"). To refresh for a newer suite, re-run the fetch+verify and update the hashes/URLs + rust_version. (sq.lock is obsolete — PGP verify is now in-process, see src/pgp.rs.)

bootstrap-seed.sh refuses to run while any lock still has TODO. It builds the backend elped (hash-pinned cargo), then — if the frontend’s opam deps are present — the frontend elpe, then runs Phase B (elpe build build.ml) twice and asserts the rebuilt daemon is bit-for-bit reproducible (§6.3/§9.10 — the real closure test; note the cargo-built seed hash and the elpe-built hash differ legitimately). Without the opam deps it still builds the backend seed and reports PARTIAL.

Lock files

FilePins
toolchain.lockrustc/cargo/protoc + gcc/clang/libc6-dev metapackages (SHA256)
vm.lockpristine Ubuntu cloud image for the VM test (SHA256 + signed SHA256SUMS)
crates.lockpointer to Cargo.lock (source of truth for 323 crate SHA256)
elpe.SHA256SUMSreproducible Phase-B elped hash (written on PASS)
sq.lockobsolete (PGP verify now in-process, src/pgp.rs)

Why not just cargo?

We use cargo build only for stage0 (to make the first elpe without a pre-existing elpe). cargo is never used as the compiler afterwards: elpe’s rust.ml calls rustc directly (rust.ml:348,360); cargo is only used for cargo metadata --offline (rust.ml:168). So the cargo path here is the bootstrap floor, not the steady state.