Customising an image
peiso's job is to make a custom Peios image an ordinary thing to build. The release stays what the edition says it is; everything you add on top is yours, and peiso writes it all down in customisations.toml beside the build so two images from the same release differ auditably rather than silently.
This page goes through the additions in the order the build applies them. All of them are optional.
Extra packages #
[[]]
= "org.gnu.bash"
= ">= 5.2" # optional
Resolved together with the edition's closure from the spec's sources, so a conflict fails the build. See The spec.
Files #
[[]]
= "overlay/motd" # relative to the spec
= "usr/etc/motd" # inside the root
= "O:SYG:SYD:(A;;GA;;;SY)(A;;FR;;;WD)" # optional
The file is copied into the composed root before anything is packed, so a dest under boot/initramfs/ lands in the initramfs — a hook you are still writing, say — and one under lcl/ lands in the operator tree. A file at a dest the release already has replaces it.
A program stays a program: a file whose source anyone can execute is injected with mode 0755, and any other with 0644. The mode means nothing more than that on Peios, but Linux still refuses to execute a file with no execute bit, whatever its descriptor allows.
sddl gives the file a security descriptor. Peios has no file modes; the descriptor is the whole of a file's access control. peiso compiles the SDDL and writes it into the squashfs as the file's security.peios.sd attribute — the same route as the firmware signatures, and just as privilege-free. Without sddl the file gets whatever the mount synthesises for an object with no descriptor, which follows its parent. The initramfs carries no attributes at all, so sddl on a file under boot/initramfs/ is refused.
First-boot scripts #
[[]]
= "scripts/setup.sh"
= "50-setup" # optional; default: the file's name
The script is placed, executable, in /lcl/policy/autorun.d/, which peinit runs in order after the registry is serving and before it plans its services. peiso's own entries there are 10-apply-seeds.sh (the release's registry seeds) and 20-features.sh; a name that sorts after those runs once the release's policy is in place, which is what a script usually wants.
Whether a script runs once is the script's business. The idiom is to end with rm -- "$0" — on an installed system that is once; on the live medium the removal is an overlay whiteout that does not survive a reboot, so the script runs again against the fresh tmpfs registry, which is usually what a live image needs. The script runs with peinit's identity at that phase, not as a logged-in user.
Features #
[[]]
= "dynamic-boot"
= "enable" # or "install"; default enable
A feature is set-up a package's scripts do beyond its files. This generates 20-features.sh, which runs feat add <name> (install then enable) or feat install <name> per entry, in order, and removes itself. The feature's definition must be in the root — usually by a [[package]] that ships it (dev.peios.feat-dynamic-boot here). Services a feature creates start on the same boot, since autoruns finish before peinit enumerates them.
The dynamic-boot definition creates mkirf and mkuki foreground watcher services. It deliberately follows the unique kernel below /usr/lib/modules rather than recording the release present when the feature was installed, so a kernel package upgrade is adopted automatically. The UKI watcher prefers the installed system command line at /lcl/etc/boot/cmdline; a live image that has not been installed falls back to /usr/share/live-boot/cmdline.
Files on the medium #
[[]]
= "docs/" # a file or a directory
= "docs"
Placed in the ISO's data area beside rootfs.squashfs and repo/. The live system mounts the medium at /media/peios, so this appears at /media/peios/docs. It is on the stick, not in the system: use it for payloads an installer or a script will read from the medium, or for things a person should find on it.
The packers #
[]
= "zstd" # any mksquashfs -comp; default zstd
= ["usr/share/doc"] # root-relative; default none
[]
= ["var/state/peipkg", "lcl/conf/peipkg"] # the default; replaces it
[]
= "loglevel=7 ignore_loglevel" # appended to live-boot's command line
initramfs.exclude replaces the default list rather than adding to it, so an explicit [] packs everything; the two defaults are the initramfs root's package database and repository configuration, which belong to the real root. boot.cmdline_extra is appended to the command line the boot package ships and baked into the UKI — there is no full replacement, because the shipped line carries init=, and losing it quietly is how a custom image fails to boot.
The shipped line runs the kernel at loglevel=4, so the console carries kernel errors and worse and peinit's own boot narrative stays legible. The example above is the common override: it puts the full kernel log back. ignore_loglevel is the token that does the work — while it is set the kernel prints every message whatever loglevel says — so raising the number alone will not undo the default.
Not a knob: the ISO's label. live-boot finds the medium by it.
The record #
Every build writes customisations.toml beside the root:
# What this spec added beyond the release. Written by peiso.
[[]]
= "/home/me/site/overlay/motd"
= "usr/etc/motd"
= "O:SYG:SYD:(A;;GA;;;SY)(A;;FR;;;WD)"
[[]]
= "/home/me/site/scripts/setup.sh"
= "50-setup"
[[]]
= "dynamic-boot"
= "enable"
Together with compose.lock.toml (every package that was resolved) it is the complete answer to "what is in this image that is not in the release".