Reference
Single-page view · as markdown
The spec
Peios / Peiso / Reference
A spec is a TOML file. Unknown keys are errors. Relative paths resolve against the spec file's directory, not the current directory — which is also what lets several specs be layered into one.
[[]]
= "file://../../pkgs/_pkgsOut_/"
= ["../../pkgs/dev-signing.pub"]
[]
= "Experimental"
= "dev.peios.peios-experimental"
= "2026.8"
= "live"
= "2026-06-22T00:00:00Z"
[]
= false
[]
= []
= []
[[]]
= "org.gnu.bash"
= ">= 5.2"
= ""
[[]]
= "overlay/motd"
= "usr/etc/motd"
= "O:SYG:SYD:(A;;GA;;;SY)(A;;FR;;;WD)"
[[]]
= "scripts/setup.sh"
= "50-setup"
[[]]
= "dynamic-boot"
= "enable"
[[]]
= "docs/"
= "docs"
[]
= "zstd"
= []
[]
= ["var/state/peipkg", "lcl/conf/peipkg"]
[]
= ""
Layering #
A build may name several specs, and they are layered left to right:
Each file is read and resolved on its own first, against its own directory. A file:// repository URL, a [[file]] src, an [[autorun]] script: each means what it says where it is written, so a layer stays movable and two layers in different directories can each name paths beside themselves.
Only the layering as a whole has to be a complete spec. The two requirements no single file can be held to — an edition, and at least one repository — are checked after the merge, and the error names every layer rather than guessing which should have carried the key. So this is a spec:
# shared.toml — where packages come from. A property of the checkout.
[[]]
= "file://../pkgs/_pkgsOut_/"
= ["../pkgs/dev-signing.pub"]
# profiles/kernel-only/peiso.toml — what is being built. A property of
# this one build.
[]
= "kernel-only"
= "none"
…while neither file is one alone. That split is the point: many builds can share one statement of where packages come from without repeating it, and without a build's own file having to restate anything it does not care about.
What a layer does to the one below #
| Scalars | A layer that states one overrides; a layer silent about one leaves it alone. edition, package, version, first_boot, source_date, squashfs.compression, squashfs.level, boot.cmdline_extra, devtools.dwe. |
| Lists | Accumulate in layering order: [[packages.repository]], [[package]], [[file]], [[autorun]], [[feature]], [[medium]], registry.add, registry.remove, squashfs.exclude. Repositories are searched in that order, so a base layer's sources come before a later layer's additions. |
initramfs.exclude | Replaces rather than accumulates. Absent and empty already mean different things there — the default list, and nothing — and appending would leave a layer no way to write either. |
A single spec is just a layering of one, so nothing above changes what one file on its own means.
[[packages.repository]] #
Where packages come from. At least one is required; several are searched together, in order.
| Key | ||
|---|---|---|
url | required | A file:// path or an http(s):// URL. A relative file:// path is resolved against the spec's directory. A file:// directory holding a repository descriptor (repo.json) is used as a repository; one holding only .peipkg files is used as a directory of packages. |
keys | optional | Public key files (raw 32 bytes or PEM) of the signers of this source's packages. The medium repository must list a package's signer to carry it, and a plain directory of packages publishes no keys of its own — so a pool source needs this. |
trust_anchors | optional | Key fingerprints to verify a repository source against. Ignored for a directory of packages. |
A source is where the build gets its packages, and only sometimes where the image will get more. An http(s):// repository is written into the image's repository configuration as source<N>, numbered in declaration order. A file:// one is not: it is a path on the build machine. See the build pipeline.
[baseline] #
| Key | ||
|---|---|---|
edition | required | The edition name as written on the box: "Experimental". |
package | optional | The concrete package identity backing the edition, such as "dev.peios.peios-experimental". New qualified editions should always state it. If omitted for an older spec, Peiso derives the historical name as peios- plus the edition lowercased with whitespace replaced by hyphens: "Pro Desktop" → peios-pro-desktop. |
version | optional | The edition version to build, e.g. "2026.8". Absent means the newest available. Written without the packaging revision; 2026.8 matches 2026.8-1 and any later revision. |
first_boot | optional, default "live" | What the medium boots into. "live" adds live-boot to the root. "none" says the composition is not a medium at all and adds no boot flavour — for a root something else will boot, or make bootable its own way; peiso iso refuses it. "install" is reserved and currently refused. |
source_date | optional | An RFC 3339 timestamp stamped on everything the build produces — compose's source date, the repository indexes, every squashfs entry, the ISO. Absent means the build time, which is not reproducible. |
[devtools] #
| Key | ||
|---|---|---|
dwe | optional, default false | Add the Developer Workstation Environment: dev.peios.dwed in the root and the live-only dwed-service seed. The package is listed as medium-only and removed from the installed target by either installer. The build directory and ISO gain a -dwe suffix. A DWE image is ownable by whoever can reach its vsock; it is for development. |
[registry] #
Adjust the seeds the edition auto-applies. Names are seed names — login-console, not login-console.reg — resolved to /usr/share/regim/<name>.reg in the composed root.
| Key | ||
|---|---|---|
add | optional | Seeds to stage in addition to the release's. Each must be shipped by a package in the root; a name nothing ships is an error. |
remove | optional | Seeds from the release's list (or from add, or the DWE seed) not to stage. |
The starting list comes from the edition's release.toml, not from the spec. A spec never has to name a seed the release already applies.
[[package]] #
A package to carry beyond the edition. Repeat the table for each.
| Key | ||
|---|---|---|
name | required | The package name, resolved from the spec's sources with its dependencies. |
version | optional | A constraint in peipkg's grammar — "5.2", ">= 5.2", ">= 5.2, < 6". Absent means any version, newest wins. |
root | optional | "initramfs" to place the package in the initramfs root — an extra boot hook, say. Absent means the main root. |
An addition rides on the same resolution as the edition, so a package that conflicts with the edition's closure fails the build rather than quietly displacing something. There is no way to remove a package: the edition's closure is what makes the image a Peios, and everything else is here only because a [[package]] put it there.
[[file]] #
A file injected into the root, outside any package. Customising an image discusses the cost.
| Key | ||
|---|---|---|
src | required | Host path, relative to the spec; must exist and be a file. |
dest | required | Path inside the root: relative (a leading / is dropped), cleaned, and not escaping the root. |
sddl | optional | The file's security descriptor in SDDL, written into the image as security.peios.sd. Refused for a dest under boot/initramfs/. |
[[autorun]] #
A script placed executable in /lcl/policy/autorun.d/.
| Key | ||
|---|---|---|
src | required | Host path, relative to the spec. |
name | optional, default the file's name | The entry's name in the directory; ordering is by name. peiso's own entries are 10-apply-seeds.sh and 20-features.sh. |
[[feature]] #
| Key | ||
|---|---|---|
name | required | A feature under /libexec/features/, present in the root. |
state | optional, default "enable" | "enable" runs feat add (install then enable); "install" runs feat install only. |
[[medium]] #
A file or directory placed in the ISO's data area, reachable at /media/peios/<dest> on the live system.
| Key | ||
|---|---|---|
src | required | Host path, relative to the spec; a file or a directory. Symlinks are refused — an ISO9660 data area cannot carry them. |
dest | required | Path on the medium, relative and cleaned. |
[squashfs] #
| Key | ||
|---|---|---|
compression | optional, default "zstd" | Passed to mksquashfs -comp. |
level | optional, default the compressor's own | Passed to mksquashfs -Xcompression-level; must be a level the chosen compressor accepts. A low level trades image size for build time — the knob a dev layer sets, never a release. |
exclude | optional | Root-relative paths left out of the image (a directory takes its subtree). |
[initramfs] #
| Key | ||
|---|---|---|
exclude | optional, default ["var/state/peipkg", "lcl/conf/peipkg"] | Globs, relative to the initramfs root, that mkirf leaves out. Setting it replaces the default; [] excludes nothing. |
[boot] #
| Key | ||
|---|---|---|
cmdline_extra | optional | Appended to the kernel command line the boot package ships, and baked into the UKI. There is no full replacement. |
What the spec cannot say #
No package list, no output paths, no ISO label, no full command-line replacement. Everything the spec adds — packages, files, scripts, features, medium contents — is an addition to a release, recorded in customisations.toml; nothing in it can redefine what the release is, and nothing in it can quietly produce a different image from the same file. What a Peios contains is the edition package's to say; peiso builds what it says, plus what you asked for on top.
The build directory
Peios / Peiso / Reference
Every build writes to one directory, named for what was resolved: dist/peios-<edition>-<version>/, with -dwe appended for a development medium. The version is the edition package's without its packaging revision — 2026.8, not 2026.8-1. The directory is relative to where peiso is run; peiso root --out overrides the root's location only.
A build refuses to run if root/ already exists; --overwrite replaces it (only a composed root or an empty directory — never anything else the output path may name). make iso passes the flag, so rebuilding in place is its normal behaviour.
| Entry | Written by | |
|---|---|---|
compose.toml | compose stage | The generated compose manifest: sources, the initramfs root, the edition and extra package requests. Marked "do not edit". |
compose.lock.toml | compose stage | The resolved closure: every package, version, root, source and hash. What the image is, exactly. |
root/ | compose stage | The composed system. root/boot/initramfs/ is the initramfs root; root/var/state/peipkg/ its database; root/lcl/conf/peipkg/ its repository configuration. |
sidecars.jsonl | compose stage | The signature sidecars compose recorded instead of stamping: one {path, blob} per line, path relative to root/. Consumed by the squashfs stage. |
customisations.toml | customise stage | Every file, autorun and feature the spec added, with the host paths they came from. |
repo/ | medium stage | The medium repository: descriptor, indexes, keys, and the disk-boot packages. Copied onto the ISO as repo/. |
root/lcl/conf/peipkg/peios-medium.repo | medium stage | The client configuration for it, anchored on this build's key. |
root/lcl/policy/autoapply.d/*.reg | seeds stage | The staged seeds. |
root/lcl/policy/autorun.d/10-apply-seeds.sh | seeds stage | The autorun that drains them at boot. |
root/system/boot/initramfs.cpio.zst | initramfs stage | The packed initramfs, inside the root. |
rootfs.squashfs | squashfs stage | The live root filesystem, with signature attributes. Outside root/ so it cannot contain itself. |
root/boot/efi/EFI/BOOT/BOOTX64.EFI | UKI stage | The unified kernel image. root/boot/efi/ is the ESP tree. |
peios-<edition>-<version>[-dwe].iso | ISO stage | The medium. |
The Makefile adds its own files beside the spec, not in the build directory: disk.img (the install target), ovmf-vars.fd (firmware variables, reset on every boot), and console.log from drive.py.
Only the ISO is needed to boot or install. root/ and rootfs.squashfs are kept because they are what you look at when something is wrong; compose.lock.toml is kept because it is the provenance.