1 LainOS Layer 02 Developer Guide
Grayson Giles edited this page 2026-06-25 03:14:33 +02:00

LainOS Layer 02 — Architecture & Developer Guide

Last Updated: 2026-06-25
Status: Active Development — Alpha


Overview

LainOS Layer 02 is built from five distinct components that work together to produce a bootable, installable ISO. Understanding how they relate to each other is essential before making changes to any of them. A change in one component almost always requires a rebuild in one or more others.

┌─────────────────────────────────────────────────────────────┐
│                      ISO Framework                          │
│         (mkarchiso profile, packages, airootfs)             │
│                                                             │
│  ┌─────────────────┐      ┌──────────────────────────────┐  │
│  │ Calamares Config│      │     protocol_7_repo          │  │
│  │ (installer UI,  │      │  (protocol7-core, dummies)   │  │
│  │  shellprocess,  │      │                              │  │
│  │  dracut config) │      │     lainos_repo              │  │
│  └─────────────────┘      │  (special included packages) │  │
│                           └──────────────────────────────┘  │
│  ┌─────────────────────────────────────────────────────┐    │
│  │              protocol7-core                         │    │
│  │  (daemons, init scripts, OpenRC services)           │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘
                              │
                    mkarchiso builds
                              │
                              ▼
                    lainOS-layer-02-*.iso

Part 1 — ISO Framework

Repository: forgejo.lain.rocks/lainOS/lainos-iso-layer-02
Local path: ~/Forgejo/lainos-iso-layer-02/lainos-iso-layer-02/
Key directory: protocol7-profile/

What It Is

The ISO framework is the mkarchiso profile. It defines everything that goes into the live ISO — which packages are installed, what files are overlaid into the live filesystem, how the ISO boots, and what the user sees when they first launch it.

Structure

protocol7-profile/
├── airootfs/                    # Files overlaid into the live squashfs
│   ├── etc/
│   │   ├── calamares/           # Calamares config (from calamares-config package)
│   │   ├── dracut.conf.d/
│   │   │   └── 99-protocol7.conf  # dracut: force_drivers, omit_dracutmodules
│   │   ├── greetd/              # Display manager config
│   │   ├── init.d/polkit        # Custom OpenRC polkit init script
│   │   └── pacman.conf          # Pacman config for the live system
│   └── usr/
│       ├── lib/dracut/modules.d/  # Custom dracut modules (35eudev-coldplug etc.)
│       └── local/bin/           # Live system scripts (wifi-scan.sh, brightness, etc.)
├── packages.x86_64              # Full package list installed into the ISO
├── pacman.conf                  # Pacman config used DURING the ISO build
└── profiledef.sh                # ISO metadata, file_permissions array

Key Files

packages.x86_64 — Every package installed in the live ISO. Protocol 7's custom packages come from protocol_7_repo and lainos_repo. Standard packages come from Arch [core], [extra], [multilib] and Artix [system], [world], [galaxy].

profiledef.sh — Defines ISO label, version, boot modes, and critically the file_permissions array. Every executable script placed in airootfs/usr/local/bin/ must be declared here with ["/usr/local/bin/scriptname"]="0:0:755" or it won't be executable in the live system.

airootfs/etc/dracut.conf.d/99-protocol7.conf — The dracut configuration that gets copied into the installed system via unpackfs. This controls which kernel modules are forced into the initramfs. Critical for NVMe boot support.

pacman.conf (profile root) — Used by mkarchiso during the build to resolve and install packages. Different from airootfs/etc/pacman.conf which is the config for the live running system.

How to Build

# Always push changes to Forgejo first
cd ~/Forgejo/lainos-iso-layer-02
git add <files>
git commit -m "description"
git push

# Clear work directory if previous build was interrupted
doas rm -rf ~/lainos-work

# Build
doas mkarchiso -v -w ~/lainos-work -o ~/lainos-out \
  ~/Forgejo/lainos-iso-layer-02/lainos-iso-layer-02/protocol7-profile

Verifying ISO Contents

After building, always verify critical files made it into the squashfs:

doas mount -o loop ~/lainos-out/lainOS-layer-02-*.iso /mnt
doas unsquashfs -cat /mnt/arch/x86_64/airootfs.sfs etc/dracut.conf.d/99-protocol7.conf
doas unsquashfs -cat /mnt/arch/x86_64/airootfs.sfs etc/calamares/modules/dracut.conf
doas unsquashfs -l /mnt/arch/x86_64/airootfs.sfs | grep "usr/local/bin"
doas umount /mnt

Part 2 — Calamares Config

Repository: forgejo.lain.rocks/lainOS/lainos-calamares-config-layer-02
Local path: ~/Forgejo/lainos-calamares-config-layer-02/
Package name: lainos-calamares-config-layer-02
Repo: protocol_7_repo

What It Is

The Calamares config package contains all the module configuration files that control how the graphical installer behaves — partitioning defaults, bootloader type, dracut options, post-install service registration, and user setup. It is built as a pacman package and installed into the ISO via packages.x86_64.

Structure

etc/calamares/
├── settings.conf              # Module execution order and instance definitions
└── modules/
    ├── bootloader.conf        # GRUB config (efiBootLoader: grub)
    ├── dracut.conf            # dracut options, initramfsName
    ├── partition.conf         # Filesystem defaults, swap options
    ├── shellprocess-final.conf  # Post-install: services, doas, user setup
    └── unpackfs.conf          # Squashfs source and destination paths

Critical Module Configs

dracut.conf — Controls how dracut generates the initramfs during install:

kernel: linux
initramfsName: "/boot/initramfs-linux.img"
options: [ "--no-hostonly", "--no-compress", "--no-uefi" ]
  • initramfsName must be set — without it grub-mkconfig generates no initrd line
  • --no-uefi prevents dracut entering UKI mode on UEFI hardware
  • Key must be options: not dracut_args: — wrong key causes silent fallback to bare dracut -f

shellprocess-final.conf — Runs shell commands in the installed system chroot after all packages are installed. Critical rules:

  • Never use ${VAR} or $VAR directly — Calamares intercepts both for its own templating
  • Never use literal newlines inside YAML strings — use printf 'line1\nline2\n' instead
  • All chroot: true commands run inside the installed system, not the live session
  • Standalone scripts must be called by absolute path, not inline

unpackfs.conf — The live mount point is always /run/initramfs/live, not /run/archiso/:

source: "/run/initramfs/live/arch/x86_64/airootfs.sfs"

How to Rebuild

cd ~/Forgejo/lainos-calamares-config-layer-02
git add etc/calamares/modules/<changed-file>
git commit -m "description"
git push   # MUST push — PKGBUILD clones fresh from Forgejo

# Bump pkgrel
sed -i "s/^pkgrel=\([0-9]*\)/pkgrel=$(( $(grep -oP '(?<=pkgrel=)\d+' PKGBUILD) + 1 ))/" PKGBUILD
grep pkgrel PKGBUILD
git add PKGBUILD && git commit -m "bump pkgrel" && git push

# Build and deploy
rm -rf src pkg && makepkg -f
cp lainos-calamares-config-layer-02-*.pkg.tar.zst /home/lain/protocol_7_repo/x86_64/
repo-add /home/lain/protocol_7_repo/x86_64/protocol_7_repo.db.tar.gz \
  /home/lain/protocol_7_repo/x86_64/lainos-calamares-config-layer-02-*.pkg.tar.zst

Then rebuild the ISO to pick up the new package.


Part 3 — Rebuilding Calamares on Boost/KPMCore Updates

Repository: forgejo.lain.rocks/lainOS/lainos-iso-layer-02 (calamares rebuild kit is inside the ISO framework repo)
Package name: lainos-calamares-dracut
Repo: protocol_7_repo

What It Is

Calamares links against boost and KPMCore (KDE Partition Manager Core). When Arch updates these libraries, the upstream Calamares binary breaks. LainOS maintains a custom Calamares build (lainos-calamares-dracut) that must be rebuilt whenever boost or kpmcore receive major updates.

When to Rebuild

Watch for pacman updates to these packages:

  • boost-libs
  • kpmcore
  • qt6-base (less common)

When they update, the installed Calamares will segfault or fail to start until rebuilt against the new versions.

How to Rebuild

The rebuild kit lives inside the ISO framework repo. Find it:

ls ~/Forgejo/lainos-iso-layer-02/

Follow the rebuild kit instructions — it patches Calamares source, builds against current system libraries, and produces a new lainos-calamares-dracut-*.pkg.tar.zst.

After rebuilding:

cp lainos-calamares-dracut-*.pkg.tar.zst /home/lain/protocol_7_repo/x86_64/
repo-add /home/lain/protocol_7_repo/x86_64/protocol_7_repo.db.tar.gz \
  /home/lain/protocol_7_repo/x86_64/lainos-calamares-dracut-*.pkg.tar.zst

Then rebuild the ISO.

Signs You Need a Calamares Rebuild

  • Calamares crashes immediately on launch in the live session
  • doas calamares shows a segfault
  • ldd /usr/bin/calamares | grep "not found" shows missing libs

Part 4 — protocol_7_repo and lainos_repo

protocol_7_repo

URL: https://gitlab.com/lainos/protocol_7_repo/-/raw/main/x86_64/
Local path: /home/lain/protocol_7_repo/x86_64/

This is the primary custom package repository. It contains:

Package Purpose
protocol7-core Core daemons, OpenRC init scripts, ghost dirs
lainos-calamares-dracut Custom Calamares build with dracut support
lainos-calamares-config-layer-02 Installer configuration
eudev Real non-systemd udev (built from upstream tarball)
Dummy packages systemd, libudev, elogind, mkinitcpio etc — zero-file packages that satisfy pacman dep chains without installing anything

Dummy packages exist because many packages declare dependencies on systemd-era packages. The dummies satisfy these dep chains so pacman doesn't refuse to install. They contain no files — just PKGBUILD metadata declaring provides= and replaces=.

lainos_repo

URL: https://gitlab.com/lainos/lainos_repo/-/raw/main/x86_64/

Contains special packages that don't exist in Arch/Artix repos or need to be patched for Protocol 7 compatibility. Examples include AUR packages pre-built for LainOS, patched versions of tools, and LainOS-specific utilities.

Managing the Repo Database

# Add a single new package
repo-add /home/lain/protocol_7_repo/x86_64/protocol_7_repo.db.tar.gz \
  /home/lain/protocol_7_repo/x86_64/newpackage-*.pkg.tar.zst

# Rebuild entire database (use after corruption or multiple additions)
cd /home/lain/protocol_7_repo/x86_64/
repo-add protocol_7_repo.db.tar.gz *.pkg.tar.zst

Never run repo-add against a single file when other packages exist — it resets the database to contain only that one package. Always use the wildcard when rebuilding.

Verifying the Repo

pacman -Sl protocol_7_repo

Should list all packages. If only one appears, the database is corrupted — rebuild with wildcard.


Part 5 — protocol7-core

Repository: forgejo.lain.rocks/lainOS/protocol-7-core
Local path: ~/Forgejo/protocol-7-core/protocol7-core-v5.5/
Package name: protocol7-core
Repo: protocol_7_repo
Current version: 5.5

What It Is

Protocol 7 Core is the heart of LainOS Layer 02. It provides the systemd compatibility surface that allows a full modern desktop to run on OpenRC. It ships six compiled C binaries and three OpenRC init scripts.

Components

Binary Version Role
lainos-dbus-bridge v4.4 org.freedesktop.login1 D-Bus facade (logind replacement)
lainos-notifyd v4.5 sd_notify socket sink
lainos-ghost-units v4.4 Creates /run/systemd/* ghost directories
lainos-init v4.6 Session initializer (called by greetd)
lainos-audio-init v4.4 PipeWire + WirePlumber + pipewire-pulse orchestrator
lainos-net-init v4.4 Kernel sysctl hardening + wireless interface prep
Init Script Runlevel Role
cgroup-delegate boot cgroup v2 verification and controller delegation
lainos-dbus-bridge default Starts logind facade
lainos-notifyd default Starts sd_notify sink

Build System

cd ~/Forgejo/protocol-7-core/protocol7-core-v5.5/

# Run the build script (handles rm -rf src pkg, updpkgsums, makepkg)
bash build_p7core.sh

Or manually:

rm -rf src pkg
updpkgsums
makepkg -sf

All binaries compile with:

-Wall -Wextra -O2 -pie -fstack-protector-strong -D_FORTIFY_SOURCE=2

Deploying

cp protocol7-core-*.pkg.tar.zst /home/lain/protocol_7_repo/x86_64/
repo-add /home/lain/protocol_7_repo/x86_64/protocol_7_repo.db.tar.gz \
  /home/lain/protocol_7_repo/x86_64/protocol7-core-*.pkg.tar.zst

Then push the repo to GitLab and rebuild the ISO.

Key Design Rules

  • Real packages only — no mock/stub shared libraries. Real systemd-libs for ABI, real eudev for udev.
  • Privilege drop pattern — every daemon: start as root → complete privileged setup → setuid(65534) → apply seccomp → enter main loop.
  • Socket readiness — never use fixed sleep/--wait delays. Poll the socket until ready.
  • No shell in daemons — use execvp() directly, never system() or sh -c.

How the Parts Fit Together

Dependency Chain

protocol7-core changes
        │
        ▼
rebuild protocol7-core package
        │
        ▼
push to protocol_7_repo
        │
        ▼
rebuild ISO (mkarchiso pulls new package)
        │
        ▼
test in KVM → test on baremetal
calamares-config changes
        │
        ▼
rebuild lainos-calamares-config-layer-02 package
        │
        ▼
push to protocol_7_repo
        │
        ▼
rebuild ISO
        │
        ▼
verify in squashfs → test install in KVM
ISO framework changes (airootfs, packages.x86_64, profiledef.sh)
        │
        ▼
git push to Forgejo (PKGBUILD clones from remote — must push first)
        │
        ▼
rebuild ISO directly (no package rebuild needed)
        │
        ▼
verify squashfs contents → test in KVM

What Triggers a Full ISO Rebuild

Every change eventually requires an ISO rebuild. The question is what else needs to happen first:

Change Type Package Rebuild? ISO Rebuild?
protocol7-core source Yes Yes
Calamares config (modules) Yes Yes
Calamares binary (boost/kpmcore) Yes Yes
airootfs file No Yes
packages.x86_64 No Yes
profiledef.sh No Yes
dracut.conf (in airootfs) No Yes
99-protocol7.conf (in airootfs) No Yes

Testing Order

Always test in this order — never skip to baremetal without KVM passing first:

  1. KVM install — confirms Calamares completes all jobs
  2. KVM boot — confirms initramfs, OpenRC, greetd, Sway all work
  3. Baremetal BIOS (Libreboot machine) — confirms real hardware NVMe boot
  4. Baremetal UEFI — confirms UEFI GRUB installation and boot

Common Mistakes

Mistake Symptom Fix
Not pushing before makepkg Old version built, fix missing Always git push before building
git add . Build artifacts staged Always stage specific files
Literal newline in shellprocess YAML shellprocess@final could not be loaded Use printf 'line1\nline2\n' — never literal newlines in YAML strings
dracut_args: instead of options: dracut runs with no options Key must be options:
Missing initramfsName No initrd line in grub.cfg, kernel panic Add initramfsName: "/boot/initramfs-linux.img"
Missing --no-uefi UEFI install fails Add to dracut options
Single-file repo-add Database shows only one package Use *.pkg.tar.zst wildcard
Script not in profiledef.sh file_permissions Script not executable in live ISO Add ["/usr/local/bin/script"]="0:0:755"
Stale work directory Build picks up cached files doas rm -rf ~/lainos-work before rebuilding
${VAR} in shellprocess command strings Calamares variable substitution error Use standalone scripts

Diagnostic Commands

# Verify package in squashfs
doas mount -o loop ~/lainos-out/lainOS-layer-02-*.iso /mnt
doas unsquashfs -cat /mnt/arch/x86_64/airootfs.sfs etc/calamares/modules/dracut.conf
doas unsquashfs -l /mnt/arch/x86_64/airootfs.sfs | grep <filename>
doas umount /mnt

# Calamares session log (from live session)
doas grep -n "" /root/.cache/calamares/session.log | sed -n 'START,ENDp'

# Inspect installed initramfs (from live session with NVMe mounted)
doas mount /dev/nvme0n1p1 /mnt
doas lsinitrd /mnt/boot/initramfs-linux.img | grep -E "nvme|udev|force"
doas lsinitrd /mnt/boot/initramfs-linux.img -f etc/cmdline.d/20-force_drivers.conf
doas grep -E "linux|initrd" /mnt/boot/grub/grub.cfg

# Verify repo database
pacman -Sl protocol_7_repo