FORGE Internals
FORGE is the InterGenOS installer. It takes a booted live environment and turns it into a deployed, bootable, user-configured operating system on a target disk. This page covers its architecture and how to contribute to it.
FORGE is distinct from the build system that produces the ISO. The build system (igos-build) compiles every package from source and assembles the signed image; FORGE consumes that image at install time and writes it to the user’s disk. For the build pipeline that creates the ISO itself, see Building the ISO below.
This is the 1.0-dev release (build id v1.0-dev1). The internals described here reflect what ships today.
Design principles
FORGE is built on three foundations.
- Declarative intent. Frontends collect user choices and emit a serialized YAML state describing the desired system. The backend consumes that YAML, plus the credentials it needs (passwords), and executes the install. The frontend never touches the disk directly.
- Supply-chain integrity. A verification gate runs before any disk write. It checks every package archive against the cryptographically signed release manifest to detect tampering, and halts the install before partitioning on any mismatch. This is the same posture the rest of the system carries: security is not first. It is only.
- Phased execution. The install runs as a linear sequence of phases. A failure halts the pipeline, performs best-effort unmounts, and surfaces the exact point of failure rather than leaving the disk in an unknown state.
Layout
FORGE lives primarily under installer/ and uses a split frontend/backend architecture united by the declarative state model:
installer/frontend/tui.py— the text frontend.installer/frontend/gui/— the graphical frontend.installer/backend/install.py— the backend orchestrator.installer/backend/hooks.py— resource context managers (mounts and similar).installer/init/init.sh— the live boot-time init that activates and verifies the root filesystem.
The two frontends
FORGE ships two user-facing frontends that execute identically against the same backend.
TUI (installer/frontend/tui.py)
A dialog-based text interface. It suits SSH-based installs, headless servers, and keyboard-driven workflows. It emits the declarative install.yaml (written to /var/lib/forge/install.yaml) and, separately, an interactive-state structure (install_io) holding the target disk and credentials, which the backend consumes alongside the YAML.
GUI (installer/frontend/gui/)
A GTK4 / libadwaita application, built as a multi-screen wizard: welcome, keyboard_locale, disk, user, packages, confirm, progress, done. It accumulates selections in installer/frontend/gui/state.py as the user moves through screens.
Security-critical interactions get dedicated handling. integrity_dialog.py disables paste (clipboard, drag-and-drop, and the right-click menu) on the integrity-override-phrase entry. The phrase must be typed in full, ensuring deliberate consent before any integrity-check override is accepted.
The backend pipeline
run_install() in installer/backend/install.py is the backend entry point. It consumes the YAML state and the interactive-state structure, then runs the 13-phase install pipeline (PHASE_ORDER):
- Validate (
PHASE_VALIDATE) — checks the YAML structure and runs deep validations (for example, regex validation of the requested hostname via_validators.py). - Verify (
PHASE_VERIFY) — the core security gate. It computes the SHA-256 of every package archive to be deployed and verifies each againstintergenos-archive-manifest.txt, which is signed by the release keys. A mismatch halts the installer before partitioning, unless the user provides explicit typed confirmation to override. - Partition (
PHASE_PARTITION) — wipes the target disk, writes a new partition table (EFI or BIOS), and formats filesystems. - Mount (
PHASE_MOUNT) — mounts the new root and boot partitions at a staging path (for example/mnt/target). - Virtual FS (
PHASE_VIRTUAL_FS) — bind-mounts the host/dev,/proc, and/sysinto the staging path forchrootoperations. - Packages (
PHASE_PACKAGES) — delegates topkmviapackages.py. Thread-queued execution extracts and registers every package named in the YAMLpackage_groups. Individual package failures are tracked but do not abort the install; FORGE surfaces a partial state instead. - Config (
PHASE_CONFIG) — generates system configuration (/etc/fstab, locale, timezone links) from the YAML intent. - Users (
PHASE_USERS) — sets the root password and creates the initial unprivileged account. - MOK / Secure Boot (
PHASE_MOK) — on an EFI system, generates a Machine Owner Key (MOK) keypair inside the target environment. - Bootloader (
PHASE_BOOTLOADER) — installs GRUB to the target disk. On EFI, GRUB is signed with the MOK keypair generated in the previous phase. - Hooks (
PHASE_HOOKS) — runs package-specific post-install scripts (font-cache updates, gsettings schema compilation, and similar). - Services (
PHASE_SERVICES) — enables the necessarysystemdservices for the target. - Cleanup (
PHASE_CLEANUP) — unmounts virtual filesystems and the target disk, copies the integrity audit log, and reports the result.
Error handling
FORGE prioritizes safe failure modes.
- Context managers. Mounts and similar resource-heavy operations are wrapped in context managers (
installer/backend/hooks.py) so unmounting happens even when an exception is raised. - Phase tracking. The
InstallResultobject tracksphase_completed. On exception, the orchestrator runs a cleanup block (_PHASES_NEEDING_UNMOUNT) scoped to how far the install reached. - User transparency. Non-fatal problems, especially trust-chain ones such as audit-log copy failures, emit warnings to
InstallResult.warnings. These show on the frontend’s final screen so the user always knows the true state of the deployment.
What gets installed
The image FORGE deploys is assembled from packages built across six tiers: toolchain, core, base, desktop, extra, and ai. Counts drift as the system evolves. As of this writing (2026-06-15) the totals are approximately:
| Tier | Packages (approx.) |
|---|---|
| toolchain | 28 |
| core | 272 |
| base | 23 |
| desktop | 420 |
| extra | 112 |
| ai | 2 |
| Total | ~857 |
To derive the live numbers for a given checkout, count the package definitions per tier rather than trusting a static figure.
The desktop that ships today is GNOME 49 on Wayland. The two ai packages are the local assistant InterGen (a tiered, hardware-detected, offline-first local assistant built on Qwen models, with zero telemetry) and llama.cpp, the inference engine it runs on. InterGen bundles a security-scanner subsystem, InterGen Sentinel, that defaults to Local-Rules plus Local-Qwen and offers six opt-in cloud providers (Claude (Anthropic), Gemini (Google), Copilot (Microsoft), ChatGPT (OpenAI), Grok (xAI), and DeepSeek). The cloud-escalation path is called Phone-A-Friend (Frontier/Cloud Escalation) and is opt-in by design.
Building the ISO
The installer is one half of the story; the other is the build system that produces the bootable image. The full build runs 20 phases:
validate -> verify-sources -> setup -> toolchain -> chroot-prep ->
chroot-tools -> core -> config -> core-extra -> base -> kernel ->
desktop -> ai -> extra -> bootloader -> image -> manifest ->
squashfs -> ukis-verity -> iso
A publish phase is optional. The final stage assembles the hybrid UEFI+BIOS ISO from the signed components.
The canonical ISO entry point is scripts/build-iso.sh, usually invoked by scripts/build-intergenos.sh, which sets its variables from its own phase state. It is env-var-driven rather than flag-driven. A representative manual invocation:
SHIM=/path/to/shimx64.efi.signed \
GRUB=/path/to/grubx64.efi.signed \
UKI_LIVE=/path/to/igos-live.efi.signed \
UKI_INSTALL_GUI=/path/to/igos-install-gui.efi.signed \
UKI_INSTALL_TUI=/path/to/igos-install-tui.efi.signed \
SQUASHFS=/path/to/filesystem.squashfs \
OUTPUT=build/intergenos-1.0-dev1.iso \
SOURCE_DATE_EPOCH=$(date -u +%s) \
bash scripts/build-iso.sh
Set SOURCE_DATE_EPOCH explicitly whenever reproducibility matters: the script otherwise falls back to current time with a warning, and a deliberate value makes the ISO bit-identical across rebuilds.
build-iso.sh runs six internal phases: stage the ESP layout, build the FAT32 ESP image (mkfs.vfat -F 32 ... -n IGOS_ESP), stage the ISO9660 root (dropping filesystem.squashfs, filesystem.verity, and filesystem.sha256 under /live/), invoke xorriso (requires 1.5.6+ for SOURCE_DATE_EPOCH), self-verify the GPT and El Torito structures, and emit a <OUTPUT>.manifest recording input/output SHAs and tool versions.
The trust chain
The signature chain shim -> GRUB -> UKI is covered by Secure Boot, but the root filesystem lives outside the UKI at /live/filesystem.squashfs. Without independent verification an attacker could swap the squashfs and still boot a trusted UKI. InterGenOS closes that gap with two paired checkpoints that must stay in lockstep:
- Primary — dm-verity. The live UKI’s sealed cmdline carries
igos.verity.roothash=<HEX>. The merkle hashtree ships on the media at/live/filesystem.verity;init.shactivates a dm-verity device and the kernel verifies each 4 KiB block as it is read. Because the root hash lives inside the Secure-Boot-signed UKI, the squashfs cannot be swapped without invalidating the chain. - Fallback — whole-file sha256.
/live/filesystem.sha256is written and verified, used only when the cmdline lacksigos.verity.roothash=(older or development UKIs). It is slower but reliable.
The build asserts that both filesystem.verity and filesystem.sha256 land non-empty, and fails otherwise. Changing one side of this pairing without the other re-opens the gap. Never remove the build-time assertion on the assumption that boot-time init will catch the problem: that turns a build-VM exit 1 into a kernel-panic-class abort on the user’s machine.
The full ISO build, signing flow, squashfs generation, and test-VM evaluation are documented in the operations topics under docs/operations/.
Contributing
FORGE is meant to be understood and modified. A few orientation points for working on it:
- Start from the phase you are touching. Both the install pipeline (
PHASE_ORDERininstaller/backend/install.py) and the ISO build (scripts/build-iso.sh) are linear and named. Find the phase, read it, change it in isolation. - Keep the two frontends equivalent. The TUI and GUI must produce the same declarative state and execute identically against the backend. A change that touches install behavior belongs in the backend, not in one frontend.
- Never relax the integrity gate quietly. The verify phase and the dm-verity / sha256 pairing are load-bearing for the whole trust model. Changes there need matching changes on the paired side and explicit review.
- Test against a VM before hardware. Build the ISO, boot it under QEMU with Secure Boot enabled, exercise MOK enrollment, and run both install frontends end to end before testing on real disks.
The goal of every change is the same as the goal of the system: a machine you understand, can modify, and can trust.
See also
- FORGE install guide — using the installer.
- Contributing — how to work in the InterGenOS tree.