Table of Contents
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" ]
initramfsNamemust be set — without it grub-mkconfig generates noinitrdline--no-uefiprevents dracut entering UKI mode on UEFI hardware- Key must be
options:notdracut_args:— wrong key causes silent fallback to baredracut -f
shellprocess-final.conf — Runs shell commands in the installed system chroot after all packages are installed. Critical rules:
- Never use
${VAR}or$VARdirectly — Calamares intercepts both for its own templating - Never use literal newlines inside YAML strings — use
printf 'line1\nline2\n'instead - All
chroot: truecommands 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-libskpmcoreqt6-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 calamaresshows a segfaultldd /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-libsfor ABI, realeudevfor 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/--waitdelays. Poll the socket until ready. - No shell in daemons — use
execvp()directly, neversystem()orsh -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:
- KVM install — confirms Calamares completes all jobs
- KVM boot — confirms initramfs, OpenRC, greetd, Sway all work
- Baremetal BIOS (Libreboot machine) — confirms real hardware NVMe boot
- 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