# USM Distro Bootstrap — Feasibility & Effort Report Investigation report for goal 15 ("a USM based Linux distro"), covering every item of the brief: a scaffold repository for LFS bootstrap packages, a bootstrap container, a USM repository of LFS packages, a DCBA repository of LFS build artifacts, a USM-driven chroot build, and a container of the final root with USM installed. Verdict up front: **feasible, with an explicitly staged architecture, and conditioned on the build host.** USM's existing mechanics (staged installs via `USM_DESTDIR`/`USM_CONFIGDIR`, destination-local resource resolution, `file://` repositories, the DCBA build-artifact flow) map well onto an LFS bootstrap — but USM's resolver is single-pass, while LFS is a three-pass toolchain construction (temporary → chroot → final), so the bootstrap cannot be one USM resolution. It must be staged: a handful of driver invocations, each a valid single-pass resolution, chained by an orchestrator. The dominant risks are environmental, not architectural: device nodes and `/proc` inside a capability-restricted chroot, systemd's build requirements, and self-hosting USM itself (valac + glib and several friends are not in the LFS book — see Stage 5). Everything below was verified against the USM checkout at commit `b046afe` ("Merge branch 'goal/dcba' of Tilo15/usm into master", branch `master`) and the LFS book checkout at branch `13.1` (LFS 13.1, `trunk` working copy), plus live probes of this build sandbox. File/line citations refer to those checkouts. --- ## 1. Executive summary - **Feasible.** Every one of the six brief items maps onto USM mechanics that exist on `master` today; none requires new USM core features. What must be built is an *orchestration layer* (a generator that turns the LFS book into USM scaffolds, stage drivers, and OCI assembly scripts) plus a small beyond-LFS package set so USM can build itself. - **The architecture must be staged.** `Resolver.resolve` (src/lib/Resolver.vala:233) processes `manage`+`build`+`runtime` phases in one pass and produces one topological order (src/lib/Resolver.vala:422-428) — a single resolution cannot express LFS's ch5/6 temporary tools → ch7 chroot tools → ch8 final system sequence, where later packages must be built *with* the products of earlier ones. The bootstrap is therefore N driven stages, each internally a normal USM install; the stages are ordered by an orchestrator script, not by the resolver. - **Host-conditioned.** On a sandbox without `CAP_SYS_ADMIN`/`CAP_MKNOD` (this one), chroot builds work but `/dev` nodes are plain files and `/proc`/`/sys` are empty. That is fine for the great majority of LFS packages (proven: bison 3.8.2 configured and compiled inside a host-root clone with exactly that setup) but endangers the systemd build and anything reading `/proc/mounts`. Final artifacts should be produced on a full host (or a rootful container); the sandbox remains excellent for development of the pipeline itself. - **Disk layout matters**: `/` (and `/tmp`) is a 17 GB overlay while `/workspace` has ~11 TB free — all LFS scratch trees must live under `/workspace`. (The host also lacks `g++`, `patch` and `makeinfo` — three chapter-2 requirements — but the sandbox has root and dnf5, so stage 0's image recipe closes that with one `dnf install` line; see §2.3.) - **Effort (LFS-only path).** Roughly **7–10 engineer-weeks** for the pipeline (generator + stage drivers + OCI scripts + key management + CI), of which the single largest item is the book-to-scaffold generator (~90 packages, ~640 command blocks, needing curated per-package handling). Wall-clock for one full bootstrap run on this 28-core sandbox: **~3–5 hours without test suites, ~8–14 hours with** (SBU-derived; see §8). Artifacts published per run: ~90 `.usmc` source packages (one per package-pass) and as many `.dcba` build artifacts, plus the signed listings. - **But a faster route exists — §5 recommends taking it.** An alternatives analysis (added after review feedback) compares seeding from a **Gentoo stage3** tarball and/or converting **Gentoo ebuilds** against mining the LFS book. Recommendation: the **hybrid** — Gentoo stage3 as the stage-0 build appliance + ebuild-derived package definitions — reaches the *same* purity (every file of the final root placed by a USM transaction; the seed is host scaffolding, exactly like the dnf-built stage 0 of the LFS plan) in **~4.5–6 engineer-weeks** (vs 7–10) and **~2.5–4 h wall clock per run** (vs 3–5 h; the temporary-toolchain passes are only ~18% of the SBU table — the saving is mostly engineering time, not build time). The LFS plan remains fully valid as the fallback and the P3-purity reference; stages 2–6 of the architecture are shared verbatim by both paths. See §5 for evidence (live stage3/ebuild probes) and §5.3 for the side-by-side numbers. ## 2. Verified platform facts Every probe below was run live in this session (2026-09-23) unless marked "inherited from the goal-15 investigation"; re-running any of them is a one-liner. ### 2.1 Capabilities and kernel - `grep CapEff /proc/self/status` → `00000000800405fb`; `capsh --decode` → `cap_chown, cap_dac_override, cap_fowner, cap_fsetid, cap_kill, cap_setgid, cap_setuid, cap_setpcap, cap_net_bind_service, cap_sys_chroot, cap_setfcap`. **`cap_sys_chroot` present; `cap_mknod`, `cap_sys_admin`, `cap_net_admin` absent** (CapBnd equals CapEff — they cannot be gained). Consequences: `chroot(2)` works; `mknod(2)` fails (`Operation not permitted`); no mounts, no user/pid namespaces, no overlayfs. - Kernel `7.0.12-101.fc43.x86_64` (uname -r) — comfortably above the book's minimum kernel requirement (the book specifies >= 5.10 for glibc's `--enable-kernel` reasoning; `chapter02/hostreqs.xml`). - chroot re-proven this session: a minimal root (`/usr/bin/true` + its `ldd` closure + the dynamic loader) executed via `chroot $W /usr/bin/true` → "CHROOT EXEC OK". - Inherited from the goal-15 investigation (not re-run): a clone of the host root with a plain-file `/dev/null` and empty `/proc` configured and **compiled GNU bison 3.8.2 with gcc inside chroot**; the only fix needed was `PATH` reconstruction under `env -i`. Also inherited: `tar` cannot carry device nodes into this sandbox (`Cannot mknod: Operation not permitted`). ### 2.2 Resources - 28 cores (`nproc`). - Storage: `/workspace` on `/dev/sdc1` — 19 T total, **11 T free**; `/` is an overlay with only **17 G free** (so `/tmp` is tight — LFS scratch trees must live under `/workspace`). - Network: outbound HTTPS works (`curl -sI https://ftp.gnu.org/gnu/` → `HTTP/1.1 200 OK`). ### 2.3 Toolchain versions and version soundness - Host gcc `16.2.1 20260819 (Red Hat 16.2.1-2)`, host glibc `2.43` (`ldd --version`), Python 3.14.7, perl 5.42.3, bison 3.8.2. - Book targets (reference/LFS-Stable `packages.ent`): gcc `16.2.0`, glibc `2.44`, binutils `2.47`, sysvinit `3.14` / systemd `261.3` (both revisions shipped in the book). - Soundness for a *native* (non-cross) bootstrap: - host glibc 2.43 < book glibc 2.44 → the temporary tools never accidentally link against a *newer* libc than they are building; - host gcc 16.2 matches the book's 16.2.0 — and crucially the book warns (`chapter02/hostreqs.xml`) that *newer* gcc/binutils than the book's are untested and likely to break the build; equal is the intended case; - kernel 7.0 > minimum → glibc headers configure cleanly. - **Host tool gaps (audited this session against the book's ch2 list):** `g++`/`cc1plus` **absent** (`gcc -x c++` fails: "cannot execute 'cc1plus'"), `patch` **absent**, and `makeinfo`/`texi2any` (Texinfo) **absent** — all three are ch2 requirements (Texinfo-5.0+ is on the list; the glibc/gcc/perl builds invoke `makeinfo` if present and skip documentation otherwise, but the book expects a compliant host). `/usr/bin/yacc` is also absent (the book wants yacc as a bison link — bison 3.8.2 itself is present, and the ch5 bison rebuild provides `/usr/bin/yacc` inside the toolchain, so this only matters for the stage-0 host surface). Everything else on the ch2 list is present: bash 5.3.9, as/ld (binutils 2.46.1), bison, coreutils (incl. `nproc`), diffutils (`diff`/`cmp`), findutils, gawk, gcc, grep, gzip, m4, make, perl, python3, sed, tar, xz. `dnf5 5.4.3.0` is present and the missing items are plain dnf installs (`gcc-c++`, `patch`, `texinfo`) → the Stage-0 image build must `dnf install gcc-c++ patch texinfo` (plus a ch2 re-check script at image-build time). ### 2.4 Container runtimes - `podman`, `docker`, `buildah`, `skopeo`, `umoci`: **all absent**. So containers must be *assembled* (tar layers + config + manifest, i.e. an OCI image layout) by script here; they can be *consumed* anywhere a runtime exists. `usm deploy`'s podman path (§4.6) remains available on hosts that have podman. ## 3. USM mechanics the bootstrap relies on (verified on master) These are the load-bearing behaviours; each was read at the cited location in this session. 1. **Staged installs.** `Paths.override_from_environment` (src/lib/Paths.vala:189-207) overrides `destination`, `prefix`, every dir, and `usm_config_dir` from `USM_DESTDIR`, `USM_PREFIX`, `USM_BINDIR`, … `USM_CONFIGDIR`. `Manifest.run_build` then calls `paths.set_envs()` (src/lib/Manifest.vala:289-314), exporting `DESTDIR/PREFIX/BINDIR/LIBDIR/…` to the build script, and `Manifest.run_install` clones the paths and overrides `destination` with the per-package install path (src/lib/Manifest.vala:441-475) before spawning the install script. `scripts/dcba-e2e.sh` drives two fully isolated roots this way end-to-end (its header documents the recipe: `/config` via USM_CONFIGDIR, `/{lists,packages,installed}` pre-created, `usm.config` with `is_managed=true` + `managed.state_path`). 2. **ResourceFinder local presence.** `ResourceFinder.locate_resource` (src/lib/ResourceFinder.vala:13-64) resolves refs mostly **relative to `paths.destination`** (`locate_path`, `locate_res`, `locate_cfg`, `locate_bin` walks `$PATH` under destination, `locate_libres`, `locate_pc` including `PKG_CONFIG_PATH` entries, `locate_vapi`, `locate_typelib`, …), so inside a chroot (destination `/`) resolution sees only the staged root. **Caveat (a real constraint, not a blocker): several locators are host-absolute** — `locate_lib` consults `ldconfig -p` and falls back to absolute `/lib /usr/lib /lib64 /usr/lib64` (src/lib/ResourceFinder.vala:75-104), and `locate_inc`, `locate_libexec`, `locate_gir`, `locate_info/man/locale`, `locate_app` use absolute `/usr/...` paths. Inside a chroot that is correct (destination is `/`); *outside* a chroot with USM_DESTDIR set, `lib:`/`include:` refs can be satisfied by the host — the stage drivers must run usm inside the target chroot, or audited for it. 3. **Resolver is single-pass.** `Resolver.resolve` (src/lib/Resolver.vala:233-287) picks roots, does ONE batched SPM query, processes all packages, and `resolution_phases` folds `manage`+`build`+`runtime` (src/lib/Resolver.vala:422-428) into one dependency graph with one topological install order. Missing resources resolve: (a) local presence, (b) system packages, (c) USM packages — exactly the semantics a stage needs ("what the previous stage left in the root counts as present"), but it cannot reorder the world between two packages in one transaction. 4. **file:// repositories.** `Repository.get_client` (src/lib/Repository/Repository.vala:34-53) routes `file` → `GioRepositoryClient`, `http`/`https` → `CurlRepositoryClient` (curl `-fsSL --max-time`). All repositories in the bootstrap can be plain directories on the same filesystem — no server needed. 5. **DCBA flow.** `usm dcba repository init/add` (src/cli/Dcba.vala): `init` generates an ed25519 keypair under `keys/` (private 0600) and a `.dcbar` descriptor; `add .dcba` validates tar.xz, derives name/version from the filename via `DcbaArtifact.parse_filename` (src/lib/Repository/DcbaArtifact.vala:22-56) and rebuilds a signed `ARTIFACTS.usml` (SHA-512 over entry lines, ed25519 signatures — `DcbaListingWriter.write`, src/lib/Repository/DcbaArtifact.vala:82+). The artifact itself is literally the package cache's `build.tar.xz` renamed: `scripts/dcba-e2e.sh` copies `/packages/-/build.tar.xz` to `-.dcba` (its §3), produced by `CachedPackage.archive_build` (src/lib/State/CachedPackage.vala:101-131, digest-gated re-archive). A consumer with the `.dcbar` configured gets the artifact as its build cache ("Retrieved pre-built artifact"), records provenance in `origin-info` (`"dcba"` field), and a tampered artifact is rejected with a warning + fallback to source build — all asserted by the e2e script (assertions a–f). 6. **`usm deploy`.** `usm manifest deploy` (src/cli/Deploy.vala:223-473) generates a Containerfile-based context and (unless `--no-build`) builds with podman and saves an xz-compressed image. Default base is Fedora (src/cli/Deploy.vala:16); `--base` overrides. This is the "container from the chroot" path on hosts with podman; here we script OCI assembly instead (Stage 6). 7. **Scaffolds.** `usm scaffold