elvboot — elevated boot
elvboot notices when somebody has been at your machine's boot path.
It is a FreeBSD verified-boot and tamper-detection project for a computer you own and carry around: a laptop whose firmware, boot medium or disk could be touched while you are not looking. The loader you boot is built, signed and deployed by you; at every boot it measures the platform it finds itself on — firmware tables, EFI variables, the images the firmware loaded, TPM counters and PCRs, the boot medium, the disk — compares them with what you declared, writes a tamper-evident record it can only write if it really ran, and tells you before it hands the disk key to the kernel. Nothing is enforced behind your back: the goal is to see manipulation, reliably, every boot, with evidence that survives a reboot.
Why would you want that? Because full-disk encryption protects data at rest, but not the code that asks for the passphrase. A modified loader, a swapped boot stick, a firmware setting changed in the night — those are the evil-maid moves, and they are invisible to a stock boot. elvboot makes them visible, with your own keys, on your own hardware, without a vendor cloud.
The family, by name:
- elebake — the tool this repository ships: the compiler of the boot trust chain. It turns your decisions into a loader, a signed boot medium and the records that judge each boot.
- platform trust gates — the loader-side engine, a FreeBSD patch series (see below): measurements, claims, gates, policies and the record chain, running inside the EFI loader before GELI and the kernel.
- earlboot / elvbootd — the runtime witnesses generated by elebake: rc.d scripts that re-check after boot (PCR agreement, expected image set) and record what the loader published.
- A boot trust chain you compile. Expectations, claims, gates and
policies are named records in a database;
stage site mk,stage build,stage signandstage deployturn them into a loader with generated C policy tables, veriexec manifests and site baselines, signed with your UEFI db key, on a removable card the machine boots from. - Measurements of the real platform. ACPI tables, EFI variables and the firmware's loaded images as learned sets (what stays the same across your boots, what moves), the TPM PCR bank, TPM reset and clock counters, NVMe power cycles, the boot origin, and the fingerprint of the GELI key the loader derived — each a claim with a verdict, each verdict in the record.
- A record chain the loader must write. An EFI variable, keyed HMAC, counter and anchors: a boot that did not run your loader leaves no valid record, and you see the gap.
- Dump and restore.
dumpwrites the whole database as an executable script of its own commands;restorereplays it. Adds are idempotent, so a replay is safe and a dump kept in git is a reproducible configuration. - Export and import. A signed, sealed pair — the dump and a bundle of the artifacts it names — attested with one OpenPGP key (a Nitrokey works), pinned on the receiving side, with serials against downgrade and provenance receipts. A second machine, a rescue medium or a fresh install can take over the exact state; this repository's own restore probe found its first real bug this way.
- Reproducible artifacts. The database is the single source of truth: every generated file (policy tables, manifests, loader.conf, earlboot scripts) is regenerated from records, builds run in isolated checkouts of a pinned source branch, signatures are deterministic. A restored database rebuilds the same chain.
- Backups and rollback with evidence. Deployed loaders are backup
records with label and description;
stage rollbacksaves the suspect loader first, then puts the known-good one back. - Emit and inspect. Every command generates shell. Pipeline
commands print it for review (append
| shto act); bookkeeping is pinned to act directly. You can read what the tool is about to do, and the test suites analyse that output instead of touching the system. - Help that cannot erode.
elebake helpand the manual page are generated from the same help blocks the architecture tests check. - One process when it counts.
elebake-binary.sh <db> <command...>runs a command, or a whole dump (batch <file>), inside one process: the anchors are functions, the emitted lines are calls. The import of a full database went from 22 minutes to 20 seconds on an old desktop.make install RUNNER=binarymakes it theelebakeyou type.elebake-compile.shis the same file by its other name and writes the script instead of running it -- what a run would do, for reading (the file's header says when that is the right tool and when it is not).elebake-walkthrough.sh <db> <command...>is the third name: the whole tree of a command as text, checks answered against the database as it is, every act shown under its anchor, nothing run -- in seconds.
elebake is nothing without the loader it compiles for. The measurement/claim/gate/policy engine that reads the compiled decisions at boot is a FreeBSD patch series, platform trust gates, and the catalogs elebake offers are parsed from the headers of that series. Against a stock FreeBSD tree the catalogs are empty and nothing measures anything.
git clone -b platform-trust-gates-15.1 \
https://github.com/johannes-bruegmann/freebsd-src.git ~/git/freebsd-src
./elebake.sh setenv ELEBAKE_FREEBSD_SRC ~/git/freebsd-src
Branch platform-trust-gates-15.1 is based on releng/15.1; the
loader-side engine lives under stand/efi/loader/local/ and its README
points back here. Parts of the series are on their way upstream.
- Detection, not enforcement. The design assumes the boot medium can be tampered with and concentrates on noticing — measurements are published, findings survive reboots, the owner decides what happens on a shortfall.
- Catalogs come from the World, records hold decisions. What the loader can measure is parsed live from the checked-out sources; what you want lives in the database as named, reusable records (expectations, claims, gates, triggers, policies) — validated sharply where arsenal meets catalog.
- Replays are an architecture principle.
dumpexports the whole database as an executable script; adds are idempotent-immutable, so restores and re-runs are safe. - No implicit defaults. The kernel that guards a machine, the license header of a generated file, the curation of a boot tree, the directory a command writes to — all explicit decisions, all fail-early when missing.
- One anchor, one thing. Commands expand their arguments to the
right until every detail is known (
stage keys→stage keys phase→stage keys policy→stage keys claim); the architecture suite refuses nested loops, dispatch inside an anchor and oversized bodies.
Working today, on real hardware: database and environment model, key
registries (pem/openpgp/pkcs11, Nitrokey signing), stages (checkout,
isolated stand/ and kernel builds, curation filter, include, manifest,
verify, attest, sign, deploy to a removable card, markers, backups,
rollback), the foundation compiler (measurement catalogs, arsenal,
per-phase policy binding, generated foundation.c), compile-time
baselines and run-time kenv records, inventory sets learned from boots
(ACPI, EFI variables, loaded images), the record chain, earlboot and
elvbootd rc.d witnesses, dump/restore, the signed export/import pair
with the minimized rescue variant, and three test suites
(architecture, unit, integration) that run in parallel.
The walk that built all this is recorded in docs/TUTORIAL.md; its
current state is the silent boot: every gate green, no claim skipped,
the database published as an attested, encrypted dump.
Open: the restore probe into a second database (in progress), the earlboot witnesses bound into custody, time windows after a series of settled boots.
docs/QUICKSTART.md— the short path.docs/TUTORIAL.md— a full guided walk, recorded live on real hardware, including the findings that walk fixed.docs/ARCHITECTURE.md— the combinator model (_terminals emit shell,__combinators re-invoke once,___batches sequence).docs/DESIGN_*.md— the design of stages and the foundation, of dump, archive and the exchange pair, of help and the manual.elebake help— fully generated, cannot erode;docs/elebake.8is rendered from the same corpus (make man).
Requirements: FreeBSD (base system tools; uefisign, gpg and a
PKCS#11 stack where the respective backends are used), the patched
source tree above, a machine with UEFI Secure Boot you control the keys
of, and ideally a TPM.
BSD-2-Clause — see LICENSE. Contributions welcome, see
CONTRIBUTING.md.