Boot and trust establishment
Single-page view · as markdown
Boot and trust establishment
Peios / Peios Security Fundamentals / Boot and trust establishment
Boot is the sequence by which Peios goes from "a kernel has been loaded and is starting" to "a fully-running system with real principals, real services, real policy". Most operating systems have a boot sequence, but Peios has a specific trust sequence — the kernel needs to establish how authority works on this running system from a starting point where no userspace exists yet, no authd is running, no directory has been consulted, and no tokens have been minted by anything but the kernel itself.
The chain of events is short and well-defined. The kernel sets up its own bootstrap tokens directly, attaches one to the first userspace process, and starts it. That first process is prelude, the PID 1 of an in-memory startup environment — the initramfs — whose job is to mount the system's real root filesystem and hand the machine over to it. peinit (signed at TCB, sitting at PID 1 of the real root) then takes over and begins launching services. Eventually authd starts and assumes responsibility for creating real identities for users and services. Each step relies on the previous; the chain has to be intact for the resulting system to be trustworthy.
This page covers the chain at a high level. Later pages cover each part in depth.
What boot establishes #
By the time boot is complete, the system has:
- Two kernel-direct bootstrap tokens (SYSTEM and Anonymous) that exist for the lifetime of the running kernel.
- A peinit process running as PID 1, signed at TCB level, holding the SYSTEM token's privileges plus knowledge of which services to launch.
- An authd process running (started by peinit), populated with the deployment's policy (privileges and claims), ready to authenticate users and services.
- A set of service processes running under tokens authd minted, each with their own identity and authority.
- An initially empty CAAP cache in the kernel. Automatic policy loading is not implemented; authd readiness does not populate it.
- Mount policies applied (set by peinit at boot).
- The handle model in steady state — services have opened the files they need; their fds have cached masks.
Once all of this is in place, the system is "up". Users can sign in (authd handles the authentication; produces tokens; peinit launches their session processes); access checks resolve normally; audit events flow through KMES; everything that the rest of these docs describes is operating.
The interesting question is what happens during boot, when each of those pieces is being established. The chain is a sequence of "this couldn't work before, but now it can":
- Before the kernel finishes init, nothing works. The kernel is itself.
- After kernel init, the kernel has constructed the SYSTEM and Anonymous tokens. The first userspace process — prelude, the initramfs PID 1 — starts running on the SYSTEM token.
- After prelude has run the initramfs, the system's real root filesystem is mounted. prelude switches
/to it and execs the real init. - After peinit takes over at PID 1 of the real root, the kernel has a userspace component that can launch other userspace components. peinit can fork-and-exec services.
- After authd starts, the system has a way to mint real identities for users and services. Until now, every process was running on SYSTEM (inherited from init); from this point on, services can run as their own identities, and users can sign in.
- The kernel can enforce central access policies explicitly installed through
kacs_set_caap. This is not an automatic boot step: distribution is unimplemented, and references to absent policies use the recovery policy. - After mount policies have been applied, FACS knows what to do on each filesystem.
Each transition is the "next thing that has to happen for the system to be coherent". They roughly correspond to the pages in this topic.
The bootstrap problem #
The deeper question: how does any of this work in the first place? authd creates tokens, but authd is itself a process — what token does it run on? peinit launches services, but peinit is also a process — what privilege does it hold to be allowed to mint child tokens? The system has to have authority and identity to do anything, but authority and identity are exactly what boot is supposed to establish.
The kernel solves this with a few "primordial" operations that don't go through the normal pipeline:
- The SYSTEM token is constructed directly by the kernel, not via
kacs_create_token. It's there from the moment the kernel finishes initialising, before any userspace process exists. - The SYSTEM token has every privilege enabled, integrity System, every group SID that any token might ever need. It's authority-maximal.
- init runs on the SYSTEM token. Every child it forks inherits the SYSTEM token until something replaces it.
- peinit is signed at TCB level (verified by the kernel at exec — the verification happens through the same signing path every binary does). Once peinit is running on the SYSTEM token, it has authority to do anything; once peinit is also signed at TCB level, it has PIP authority too.
- authd is similarly signed at TCB and run by peinit with the SYSTEM token. Authd then uses its
SeCreateTokenPrivilege(from the SYSTEM token) to mint other tokens for other processes.
The chain works because each link is held by the previous. The kernel builds the SYSTEM token (link 1); prelude, the initramfs PID 1, runs on it (link 2); peinit takes over from prelude when the system switches to the real root (link 3); peinit launches authd (link 4); authd creates real tokens (link 5). Each link can do its job because the previous link gave it the authority.
The risk: anything that breaks a link breaks the chain. A peinit not signed at TCB level wouldn't get PIP protection at exec; a compromised authd would mint malicious tokens. The signing-and-PIP machinery is what makes the chain trustworthy: each binary in the chain is one whose signature the kernel verifies, and that signature anchors the kernel's trust in what the binary does next.
The "everything is SYSTEM" phase #
There is a brief window during boot where every userspace process is running on the SYSTEM token. From prelude starting in the initramfs, through the early phases of peinit, up until peinit starts launching real services as themselves, every process is SYSTEM.
This is a feature, not a bug. The SYSTEM token is authority-maximal. Whatever early-boot work needs to happen — reading configuration, populating mount policies, launching authd itself — can be done without worrying about whether the running process has the right privileges. It does, because everything is SYSTEM.
The window is short. peinit aims to launch authd as quickly as possible because most of the system is not useful until authd is running. From the moment authd is up, identities can be real, services can run as themselves, users can sign in. The SYSTEM-everywhere phase is what enables the bootstrap, then ends as soon as the real identity machinery can take over.
Services that must not start before authd is ready include anything that handles user requests. A file-server that started in the SYSTEM phase would have its access checks running as SYSTEM, not as the principal it should be acting for — which is operationally wrong even though it's authoritatively allowed. peinit waits for authd to be ready before launching such services.
Services that may start before authd include things that only need their own state — the registry daemon, observability daemons. These can run as SYSTEM for the rest of the boot if they need to, then ideally drop privileges or get reassigned a more specific token once authd is up.
The exact ordering — which services need to wait for what — is part of peinit's startup configuration, not a kernel concern.
Where to start #
If you want the kernel-direct bootstrap tokens — what SYSTEM and Anonymous contain, why they're constructed without going through kacs_create_token, how they relate to the rest of the token model — read Bootstrap tokens.
If you want the initramfs stage — prelude, the in-memory startup environment, the /boot/initramfs/ directory it is built from, and the handoff to the real root — read The initramfs stage.
If you want peinit's role — what makes it the right thing to be PID 1, the service-launching pattern, the lifecycle-manager role that falls out of PIP — read peinit at PID 1.
If you want the authd transition — what authd does at startup, the CAAP distribution limitation, the SYSTEM-everywhere-handoff — read authd handoff.
Optional implementation references #
If you want how the initramfs is composed — boot hooks, the capabilities they declare, how their order is resolved, and how to write one — read Boot hooks.
If you want the kernel-level invariants that the boot chain depends on — the LSM stack, the build config flags, what the kernel refuses to do — read Kernel invariants.
The boot artifacts themselves are built by two command-line tools, the two halves of Peios' Dynamic Boot system. mkirf compiles the /boot/initramfs/ source tree into the deterministic initramfs image; mkuki then wraps that image, together with a kernel and command line, into the single UEFI unified kernel image the firmware boots. Both can run once or stay resident, keeping the boot image current as their inputs change.
Bootstrap tokens
Peios / Peios Security Fundamentals / Boot and trust establishment
The kernel constructs two tokens directly during initialisation, before any userspace process exists. These are the SYSTEM token and the Anonymous token. Both exist as singletons in the kernel from the moment kernel init completes; both persist for the lifetime of the running kernel; both are created without going through kacs_create_token (there is no userspace caller to make that syscall).
The two tokens have opposite roles. SYSTEM is the authority-maximal token — every privilege, every well-known administrative group, integrity System — that gets attached to init and bootstraps every other userspace token. Anonymous is the identity-minimal token — no real principal, no privileges, integrity Untrusted — that backs the Anonymous-level of impersonation.
This page covers what each contains and why they're constructed this way.
SYSTEM — authority-maximal bootstrap #
The SYSTEM token is what init runs on. Specifically:
| Field | Value |
|---|---|
user_sid | S-1-5-18 (the SYSTEM well-known SID) |
groups | BUILTIN\Administrators (with SE_GROUP_OWNER), Everyone, Authenticated Users, Local, and the bootstrap logon SID S-1-5-5-0-999 |
integrity_level | S-1-16-16384 (System integrity — the highest) |
mandatory_policy | NO_WRITE_UP |
privileges | Every privilege defined in the catalog, all present, all enabled |
token_type | Primary |
impersonation_level | Delegation — the top of the ratchet, so nothing derived from SYSTEM is capped by it |
auth_id | 0 (the SYSTEM session) |
default_dacl | Grants SYSTEM and BUILTIN\Administrators GENERIC_ALL; denies everyone else by absence |
The SYSTEM token is the most powerful identity on a running Peios system. It has every privilege, full administrative-group membership, the highest integrity. The only thing it lacks is a specific real principal — it's a system-internal identity, not a user.
The SYSTEM token has session ID 0, which corresponds to the SYSTEM session (also kernel-constructed). The session is a peer of the token; both exist together from kernel init.
The default DACL on the SYSTEM token is restrictive in an important way: when SYSTEM creates a new object without specifying an explicit SD, the default DACL ends up on the object. Granting only SYSTEM and Administrators means objects SYSTEM creates are administrator-only by default. This is the right policy for things like service configuration files — they should not be readable by every user just because they were created during boot.
Anonymous — identity-minimal singleton #
The Anonymous token has the opposite shape. It's not used for running processes; it's used as the identity of Anonymous-level impersonation tokens (the lowest impersonation level — see Impersonation levels).
| Field | Value |
|---|---|
user_sid | S-1-5-7 (the Anonymous well-known SID) |
groups | Everyone only |
integrity_level | S-1-16-0 (Untrusted — the lowest) |
mandatory_policy | NO_WRITE_UP |
privileges | None |
token_type | Impersonation (it exists specifically for impersonation) |
impersonation_level | Anonymous |
auth_id | 998 (the Anonymous session) |
The Anonymous token has the bare minimum to be a token at all. No real principal (the user SID is the well-known "Anonymous" placeholder). Just Everyone for groups — explicitly not Authenticated Users. No privileges. Untrusted integrity.
When a client connects to a server and sets the impersonation level to Anonymous (or the gates downgrade the impersonation level to Anonymous), the server's effective token becomes a derivation of this Anonymous token — essentially a copy with the relevant session bindings. The kernel's "Anonymous identity" is centrally defined here; the rest of the system inherits from it.
Why kernel-direct construction #
The two tokens are constructed by direct kernel initialisation, not via kacs_create_token. This matters because kacs_create_token requires SeCreateTokenPrivilege — which requires a caller — which requires a token — which is exactly what's being established. The bootstrap problem.
The kernel breaks the cycle by constructing these two tokens with internal code that doesn't go through the usual API. The kernel can do this because it is the kernel; there's no privilege check on itself.
The implications:
- No record exists of who minted these tokens. The
sourcefield, normally set to whatever component calledkacs_create_token, is set by the kernel to indicate "kernel-internal". The tokens were not minted by authd, peinit, or any other component. - They cannot be re-created at runtime. Even though both tokens have the same shape every time they're constructed (the values are kernel-internal constants), no userspace component can ask the kernel to construct another SYSTEM token at the same level. SeCreateTokenPrivilege lets userspace create new tokens; it doesn't grant access to construct kernel-internal singletons.
- They are singletons. There is exactly one SYSTEM token and one Anonymous token at any moment in the kernel. Other tokens may reference them or be derived from them, but the canonical instances are unique.
The kernel-direct construction is the foundation: it gives the rest of the system a starting identity to build from. Once SYSTEM exists and is attached to init, everything that follows can use normal API calls — they have a token, they have privileges, they can mint other tokens.
What's attached to what #
At the end of kernel init:
- SYSTEM token. Constructed; ready.
- Anonymous token. Constructed; ready.
- SYSTEM session (ID 999). Constructed; the SYSTEM token is bound to it.
- Anonymous session (ID 998). Constructed; the Anonymous token is bound to it.
- init process. About to start; will have the SYSTEM token attached to it.
Init starts and takes the SYSTEM token as its primary token. The first userspace process the kernel runs is prelude, the PID 1 of the initramfs — the in-memory startup environment that mounts the real root (see The initramfs stage). The SYSTEM token survives every exec along the way: prelude runs on it, and when prelude hands the machine to the real root it execs peinit, which takes over with the SYSTEM token as its own.
From this point on, the only entity with the SYSTEM token is peinit (and anything peinit chooses to fork before assigning each child its own token). When peinit launches authd, it does so by forking and then either letting the child inherit SYSTEM (briefly, until authd starts assigning a different token to itself) or by replacing the token with one peinit prepared.
The Anonymous token is not attached to any process. It exists as a kernel-internal object; impersonation tokens at the Anonymous level are derivations of it but the canonical instance lives in the kernel.
Lifecycle #
Both bootstrap tokens are never destroyed during the kernel's lifetime. They are reference-counted like any other tokens, but their references never drop to zero because:
- The SYSTEM token has at least one attachment (initially init, then peinit, then whatever process inherits from peinit). Even after peinit assigns specific tokens to most child processes, peinit itself continues to run on SYSTEM.
- The Anonymous token is a kernel-internal singleton. The kernel holds a reference to it; the reference is not dropped until kernel shutdown.
On reboot, the kernel re-constructs them at init. They are not persistent — there is no on-disk SYSTEM-token record — they are re-built fresh every boot. The fields are deterministic, so the new instances are equivalent to the previous ones structurally; the actual token instances are different objects.
The session IDs (999 for SYSTEM, 998 for Anonymous) are also fixed across boots. The logon SID derived from the SYSTEM session is S-1-5-5-0-999.
Why these specific shapes #
The SYSTEM token has every privilege and full administrative-group membership because it needs to be able to do anything. Boot involves operations that require a wide range of privileges — SeLoadDriverPrivilege to load kernel modules, SeBackupPrivilege / SeRestorePrivilege for early-system manipulation, SeShutdownPrivilege for orderly shutdown. Restricting SYSTEM would force peinit to know in advance which privileges it would need, which is brittle.
SeBackupPrivilege and SeRestorePrivilege are special — they're on the SYSTEM token at boot, but peinit typically calls FilterToken to remove them when assigning tokens to services that don't need them. SYSTEM having them is the starting point; the actual services usually run without them.
The Anonymous token has the opposite design: nothing it shouldn't need. The Anonymous identity is what gets used when a caller has not authenticated, and granting it any group beyond Everyone would risk accidentally giving anonymous callers access to resources whose DACLs name those groups. Empty privileges, Untrusted integrity, no Authenticated Users membership.
The two tokens together establish the trust range. SYSTEM is "the most trust the system can confer"; Anonymous is "the least trust meaningful". Every other token sits somewhere between, with parameters set by authd from the directory.
What the bootstrap tokens are not #
A few clarifications:
- They are not user accounts. SYSTEM is the system's own identity; Anonymous is the placeholder for "no identity". Neither corresponds to a user that signed in.
- They are not interchangeable with administrator tokens. A real user in the
BUILTIN\Administratorsgroup has a different token from SYSTEM — same group membership conceptually, but with the user's actual user_sid, the user's session, etc. SYSTEM and Administrators are not the same identity. - They don't persist across reboots. Every boot constructs fresh instances. Anything keyed to the SYSTEM token's instance ID (which is rare; nothing should be) would lose its reference at reboot.
- They cannot be modified. Almost everything about them is immutable. AdjustPrivileges would technically be possible on SYSTEM (a token holder of SYSTEM could enable/disable privileges, or even remove them) but the kernel makes such adjustments a one-time event for the bootstrap token, and the field-level operations are constrained. In practice nothing modifies the SYSTEM token directly; peinit makes filtered copies for child tokens.
Where to go next #
For the first userspace process the SYSTEM token is handed to, read The initramfs stage.
For the moment real identities start replacing SYSTEM, read authd handoff.
For what a token is in general — types, fields, lifecycle — read Tokens.
The initramfs stage
Peios / Peios Security Fundamentals / Boot and trust establishment
The kernel cannot reach the real root filesystem on its own. By the time it has finished its own initialisation it can speak to a CPU and some memory, but the disk the system is installed on may sit behind a storage driver that is not yet loaded, a volume manager that has not been assembled, or an encrypted container that has not been unlocked. Mounting that filesystem is work, and it is work that has to be done in userspace — the kernel does not mount real roots by guesswork.
So there is a stage in between. The bootloader loads a second thing into memory alongside the kernel: a small, complete root filesystem called the initramfs. The kernel unpacks it into a RAM-backed filesystem, makes that filesystem /, and starts a PID 1 inside it. This in-memory system exists for one purpose — to get the real root mounted and then get out of the way. On Peios, the PID 1 of that in-memory system is prelude.
The initramfs stage sits between two of the other pages in this topic. Bootstrap tokens covers what the kernel constructs before any userspace exists; peinit at PID 1 covers the init system that runs on the real root. prelude is what runs in between — the first userspace process the system ever has, and the last thing that runs before the real system begins.
prelude in the trust chain #
prelude is the first userspace process on the machine. The kernel attaches the SYSTEM token to it — prelude is the "init" that Bootstrap tokens describes the SYSTEM token being handed to. From the moment prelude starts, the "everything is SYSTEM" phase of boot (covered in the overview) is underway: inside the initramfs there is exactly one identity, SYSTEM, and exactly one job, getting to the real root. There is no directory, no authd, no notion of separate principals — and no need for one. The initramfs is a single-purpose, single-identity environment.
The initramfs's integrity is the integrity of the initramfs image itself. The image is a sealed archive: once it is built, nothing edits it in place. It is read into RAM at boot, used once, and discarded at the handoff — there is nothing inside it to tamper with at runtime. Under Secure Boot (a later milestone), the firmware verifies the whole boot artifact — kernel and initramfs together — before the kernel is allowed to run at all, so the sealed image is also a verified one.
prelude — the initramfs PID 1 #
prelude is a small, dedicated init. It is not a general-purpose process manager and it is not peinit: peinit supervises services for the system's entire lifetime, whereas prelude runs one short, fixed sequence and then replaces itself with the real init. They are separate programs with separate jobs. The name reflects the relationship — prelude is the short piece that runs before peinit's main one.
What prelude itself does is deliberately minimal. It is the invariant skeleton of the boot — the part that is identical on every Peios machine. Preparing the kernel's virtual filesystems, running the hooks, checking that a root was mounted, switching to it: that is the whole of prelude. Everything that varies between machines — which storage driver is needed, whether there is disk encryption, what kind of filesystem the root is — is not in prelude at all. It is in boot hooks.
This division is the important design point. A machine that boots from a plain disk, a machine that boots from an encrypted volume, and a machine that boots over a network differ only in their hooks. prelude is the same binary on all three. It never has to change to support a new kind of deployment — a new deployment is a new hook, not a new prelude.
Where the initramfs comes from #
The initramfs is not a mysterious binary blob. It is compiled from an ordinary directory on the real root filesystem: /boot/initramfs/. Whatever is in that directory becomes the contents of the in-memory root. The directory is the source; the initramfs image is the build product.
The layout is straightforward:
/boot/initramfs/
init symlink to the prelude binary — the initramfs PID 1
usr/
sbin/prelude the prelude binary itself
bin/
dash the shell; sh -> dash provides /usr/bin/sh
peiosutils one multi-call binary backing mount, chroot, ls, … via symlinks
lib64 -> usr/lib/x86_64-linux-peios
the x86-64 ABI loader path; /bin, /sbin and /lib views
do not exist before the runtime topology is mounted
usr/libexec/prelude/hooks.d/
mount-root.sh mounts a live medium's squashfs (live-boot), or
mount-root-disk.sh
mounts an installed root partition (disk-boot). Their
packages conflict, so exactly one is present; the
selected hook contributes rootfs-ready.
mount-rootfs-stratafs-base.sh
mounts the real root's StrataFS views after rootfs-ready
...
lcl/libexec/prelude/hooks.d/
operator-placed hooks; a file here replaces a packaged
hook of the same name
It is a normal directory. You can list it, read it, and see exactly what the initramfs will contain — inspecting the boot environment is ls /boot/initramfs/, not unpacking an archive. This is intentional: the initramfs should feel like part of the filesystem an administrator already understands, not a separate, opaque build system.
Most of what lands in the directory is put there by packages. A boot feature — disk encryption, an exotic storage backend — is an ordinary peipkg; installing it drops its hook (and any helper binaries) into /boot/initramfs/, and removing the package takes them away. There is no separate "initramfs configuration" to edit and no central list of supported features: the directory is the configuration, and a package contributes to the boot simply by installing files into it. Boot hooks covers this in full.
Because hooks are shell scripts, the initramfs has to carry a shell. That shell is dash: it provides the sh capability prelude depends on and supplies /usr/bin/sh. A hook must name an interpreter available when it runs; /usr/bin/sh is not a universal hook requirement. See the hook-authoring guidance for the image-specific distinction. The ordinary utilities a hook invokes — mount, chroot, ls, and the rest — come from peiosutils, a single multi-call binary that backs each tool through a symlink (mount, chroot, and friends are all the one binary, invoked under different names). Driver- or filesystem-specific tools a particular hook needs — blkid, a mkfs helper — arrive with the feature package that ships that hook, not with the base initramfs. Module loading is the exception: modprobe comes from kmod and the modules themselves from kernel-modules-irf, both base packages rather than per-feature ones, because any hook that touches storage may need them.
Kernel modules in the initramfs #
The initramfs carries its own set of kernel modules, shipped by the kernel-modules-irf package. It is a separate root from the real one and shares nothing with it, so it needs its own copy of anything it uses — the same reason it needs its own mountpoints and its own loader link.
The set is deliberately a subset. The initramfs only has to reach the root filesystem, so it carries storage controllers and block devices, the input drivers a passphrase prompt needs, filesystem and crypto drivers, and network drivers for a root reached over the network. It does not carry graphics, sound, media or wireless: the real root can load those for itself once it is mounted, and everything in the initramfs is paid for on every boot — it is loaded whole into memory, and under a UKI it sits inside an image the firmware verifies in one piece.
The set is closed under its own dependencies: the build indexes it with depmod -e against the kernel's symbol table and fails if any module in it imports a symbol that neither the kernel nor another module in the set provides. That matters because a subset is exactly where an ordinary index goes quiet — depmod cannot record a dependency it cannot resolve, so a module whose provider was left out indexes cleanly and fails at boot with an unknown symbol. Every module the initramfs carries is therefore one it can load.
The set is also generic: every machine gets the same modules, rather than a set tailored to the hardware it was built on. Two things make that the only workable choice. Composing a root resolves offline and runs no install-time side effects, so nothing in that path may inspect the hardware it is composing for. And because a UKI is signed as a single image, a per-machine initramfs would have to be assembled and signed on the target — which would mean a signing key on every installed machine. A generic initramfs is also what mainstream distributions default to; tailoring it is the opt-in, not the norm.
A module being available in the initramfs is not the same as it being loaded, and the kernel never loads a driver for hardware on its own: it enumerates the devices and emits a uevent for each one naming the module that can drive it, but binding a driver to a device is userspace's job. In the initramfs that job is done once, by the coldplug hook that the dev.peios.coldplug-irf package ships. It runs after the initramfs's own views are assembled and before any root-mount hook, reads the modalias of every device the kernel has enumerated under /sys/bus, and hands each newly observed set to modprobe. It rescans through a bounded settling window because loading one bus driver can enumerate another wave of devices — a modular USB controller revealing a USB storage device, for example — and there is no hotplug daemon in the initramfs to catch that second wave. A machine whose root sits behind a modular storage driver therefore has that driver loaded by the time the root-mount hook goes looking for the disk. Aliases that match no module are the normal case and are ignored; a module that fails to load is reported and is not, on its own, a boot failure — whether the missing driver matters is for the root-mount hook to decide, because it is the one that knows which disk it needs.
The hook is a bounded one-shot and does not stay resident. It stops after the modalias set has remained unchanged for a short settling window, with an outer scan limit so broken or continuously changing hardware cannot hold the boot forever. Devices that appear later, and the stable /dev/disk/by-* names, belong to the device manager on the real root.
What prelude does at boot #
When the kernel starts prelude, it runs one fixed sequence, in order:
- Prepare the environment. prelude mounts the kernel's virtual filesystems —
/proc,/sys,/dev— so that it and the hooks can see processes, devices, and kernel state, and arranges the mount environment so the later root switch is unobstructed. It then seeds/devwith a security descriptor, which is a step with consequences well beyond the initramfs — see The descriptor prelude puts on /dev. - Determine the target init. prelude reads the kernel command line for an
init=value — the program to hand off to on the real root. The shipped image names/bin/peinit2. Ifinit=appears more than once the last one wins, as it does for any repeated kernel parameter, so you can override a bootloader entry by appendinginit=/bin/shto it rather than rewriting the line; aninit=with no value is not a value at all and is passed over, whatever its position. If the command line does not name one, prelude searches/bin/peinit2,/sbin/init,/bin/init, and finally/bin/sh. Aninit=does not replace that search, it goes in front of it: a named init that turns out not to be there is passed over, and prelude carries on down the list rather than failing. Only when every candidate has been tried does the boot stop. These are target-root runtime paths: the base-topology hook creates their StrataFS views before prelude hands off. Prelude's own initramfs rescue shell remains/usr/bin/sh, so it does not rely on a/binview. - Run the hooks. prelude creates an empty
/mnt/rootfsdirectory — the mount point the real root will appear at — and runs the boot hooks in order. The hooks do the deployment-specific work: load drivers, unlock encryption, assemble volumes, and mount the real root onto/mnt/rootfs. The packagedmount-rootfs-stratafs-base.shhook (fromdev.peios.fsbase-stratafs-mount-hooks) then mounts the conventional runtime views inside that root. prelude does none of this itself; it runs the hook list. The order was decided when the initramfs was built (see below, and Boot hooks). - Verify the real root. After the hooks have run, prelude checks that
/mnt/rootfsactually has a filesystem mounted on it. If no hook mounted a root, prelude fails the boot rather than handing off to nothing. This is the one outcome prelude insists on: some hook must have produced a mounted root. - Hand off. prelude carries the kernel virtual filesystems into the new root, frees the now-finished in-memory root to reclaim its space, switches
/to the real root, and executes the target init. From that exec onward, the initramfs is gone and the real system is running.
prelude runs this sequence exactly once. It does not loop, supervise, or stay resident — the moment the real init is exec'd, prelude has ceased to exist; the exec replaces it. Its entire lifetime is the few seconds of the initramfs stage.
The descriptor prelude puts on /dev #
Mounting /dev is the smaller half of step 1. The larger half is what
prelude does immediately afterwards: it runs seed-sd -r /dev, stamping
the devtmpfs root and every node already in it with a security
descriptor.
That is necessary because of where devtmpfs starts. The kernel gives its
root a SYSTEM-only descriptor when it initialises, which is plumbing
rather than policy — it exists so that the kernel's own device thread can
create nodes at all under DENY_MISSING, and SYSTEM-only is the right
answer for that one job. But every node created since has inherited that
single entry. On an unseeded /dev an administrator cannot list the
directory, cannot open /dev/null, and cannot touch the disk an
installer is about to format.
So prelude re-stamps the tree with the bootstrap descriptor: full control for SYSTEM and for Administrators, both inheritable, and an inherit-only entry for CREATOR OWNER. Nodes that appear later — a hot-plugged disk, partitions rescanned after a partition table is written — inherit that from the root rather than the kernel's.
The third entry is the one that is easy to overlook. Being inherit-only it grants nothing on the node it sits on; it applies to whatever is created beneath it, giving the creator full control of its own object. Without it a process that is not SYSTEM can create a file it cannot then read back, because the two entries above it are a non-empty ACL naming somebody else, and a token's own default is consulted only when inheritance yields nothing at all.
This descriptor is deliberately narrow, and it has to stay narrow.
Whatever it grants, it grants on a raw disk nobody has met yet: a read
entry for Everyone on /dev is every filesystem ACL on the machine
bypassed by opening the block device. The permissive exceptions that a
running system needs — /dev/null and its kin, writable by anyone — are
per-node policy, and stamping them is peinit's
job on the real root, not prelude's.
A failure here does not stop the boot. prelude reports it and carries
on. A /dev that is still SYSTEM-only is a degraded system rather than an
unbootable one — the recovery console still works there — and refusing to
boot would turn a permissions defect into an outage. The console line is
the only warning, so it is worth reading: it names how far the seeding
got. It is tagged [ WARN ] rather than [FAILED], because the boot goes
on to complete — the tag tells you whether the boot survived, and this one
did — and it is not removed by peios.quiet=2.
When the initramfs stage fails #
prelude does not limp. If any step fails — a hook exits with an error, no hook mounts the root, the target init cannot be found — prelude stops and halts the machine. It does not fall through to a half-configured system, and it does not try to work around a failed hook.
This is the right behaviour for the stage. The initramfs's only job is to deliver a correctly-mounted real root to a real init. A failure there means the system cannot be brought up safely; continuing would only produce a system in an undefined state. Halting makes the failure unambiguous — the console shows which step failed — and leaves the operator with a clean situation to diagnose rather than a subtly-broken running system. The one way to intervene rather than halt is the debug knobs below.
Debugging the initramfs stage #
Two kernel command-line knobs (familiar from dracut) turn the fixed sequence into something an operator can step through. prelude reads them from /proc/cmdline, the same place it reads init= — as PID 1 it has no argv, so the command line is its only input. The interactive shell they open is dash, run as sh -i on the console.
-
rd.shell— drop to a shell on failure instead of halting. If any boot step fails whilerd.shellis set, prelude opens the shell so you can inspect the half-built initramfs: see what the hooks mounted, read the log, try a mount by hand. The failure still ends the boot — when you exit the shell, prelude halts as it would have anyway. Withoutrd.shell, a failure halts immediately.This holds all the way through the handoff. prelude settles the question the moment it reads the command line, at the end of step 1, and holds the answer for the rest of the boot — so a failure in step 5, after
/prochas been carried into the new root and/proc/cmdlineis no longer where it was, still opens the shell. That is the window the flag is most worth setting for: the kernel filesystems are moved, the old root may be part-emptied, and the namespace is mid-pivot. The only failures the flag cannot answer are the two that happen before the command line has been read at all — the mount-propagation call and the/procmount itself — and prelude says so on the console rather than halting as though nothing had been asked for. -
rd.break— stop before any hook runs. Barerd.breakpauses just before the first hook and opens the shell; exit the shell and the boot continues normally. This is the point where the kernel virtual filesystems are up but nothing deployment-specific has happened yet — which includes reading the hook sequence: the breakpoint fires before prelude has looked at the manifest, so an image whose sequence is missing or unreadable still reaches the shell. That is worth knowing, because the sequence is a few lines of text at a known path, and a breakpoint shell resumes the boot when you exit it. Rewrite the file by hand and prelude reads what you wrote. -
rd.break=<hook>— stop just before a named hook. The value is matched against a hook's file name — or its full path — as it appears in the resolved boot sequence, sord.break=mount-root.shbreaks immediately before that hook and lets everything ordered before it run first. The value is comma-separated and the flag may be repeated —rd.break=modules.sh,mount-root.sh— to break before several hooks.
The rd.break shells are true breakpoints, not failures: prelude forks them, so it stays PID 1 and the boot resumes exactly where it paused when the shell exits. Setting any rd.break also implies rd.shell — so if the boot goes on to fail, you get the failure shell too.
Keeping the initramfs current #
Because the directory is the source and the initramfs image is a build product, the two have to be kept in step. Whenever /boot/initramfs/ changes — a feature package is installed or removed, the kernel is updated, an administrator edits a hook — the image has to be rebuilt from the directory.
The tool that does this is mkirf — see mkirf for the full command reference. It reads /boot/initramfs/, resolves the order the hooks must run in, checks the directory is internally consistent, and writes the compressed initramfs image. Three properties of it matter to an operator:
- It validates. mkirf will not produce an image from a directory that cannot boot. A hook set with an impossible ordering, a hook that depends on a capability nothing provides, a missing
init— each of these stops the build with a clear error. A misconfigured boot is caught when the image is built, on a running system where the message is easy to read, rather than as a mystery failure at the next boot. Boot hooks covers exactly what is checked. - It is deterministic. The same directory always compiles to the same image, byte for byte — identities, timestamps, and ordering are all normalised. This is what makes "did anything actually change?" a meaningful question, and it underpins later work such as signed boot artifacts.
- It keeps the directory pristine. mkirf reads the directory and writes the image elsewhere; it never writes back into
/boot/initramfs/. The resolved hook order is recorded inside the image, not in the source. The directory you inspect is always exactly what packages and you have put there.
mkirf can run once and exit, or run in a watch mode where it stays resident and recompiles automatically whenever the directory changes. Watch mode is what lets /boot/initramfs/ behave like an ordinary part of the filesystem — edit a hook and the initramfs is current again — rather than a build artifact an administrator has to remember to regenerate.
When more than one kernel is installed, there is one initramfs per kernel: the drivers a kernel needs are specific to its version, so each kernel gets an image built against its own modules.
The handoff to the real root #
The final act of the initramfs stage is the handoff: prelude replaces the in-memory root with the real root and gives the machine to the real init.
Concretely, the real root has been mounted at /mnt/rootfs by a hook; prelude carries the kernel virtual filesystems across into it, empties the in-memory root to reclaim its space, and then chdirs, chroots and execs the target init.
It is a chroot, and that is worth being exact about, because the operation this resembles — the one usually called switch_root — is not what happens. The kernel refuses to relocate a mount onto the initramfs rootfs, which rules out both MS_MOVE and pivot_root. So the initramfs rootfs does not go away: it remains the mount-namespace root, emptied and unreachable from anything running after the handoff, but present. What is reclaimed is the space its contents occupied, not the root itself.
That distinction matters to the init on the other side. peinit never assumes a clean single-root mount topology and never attempts pivot_root, for exactly this reason — see the initramfs contract in its manual, which describes the same handoff from the receiving end.
The init prelude execs is the one identified back in step 2 — the init= value from the kernel command line, or the fallback search. On a complete Peios system that init is peinit, which takes over as PID 1 of the real root and brings up the rest of userspace. During earlier development, before peinit exists, the target is a simpler stand-in; the contract is the same either way — prelude delivers a mounted real root and a console, and execs whatever the real init is.
The handoff is a one-way door. Once the real init is running there is no path back to the initramfs — its contents are gone and nothing running has a name for what remains; the in-memory stage exists only up to this moment. The SYSTEM token, though, survives the handoff — the real init inherits it across the exec, exactly as every program inherits its primary token across exec. That is the thread tying this page to Bootstrap tokens: the SYSTEM token the kernel attached to prelude is the same token peinit starts life holding.
What prelude does not do #
A few clarifications:
- prelude does not mount the real root itself. A hook does. prelude provides the
/mnt/rootfsmount point and verifies the result, but the mount is always a hook's job — that is what keeps prelude identical across deployments. See Boot hooks. - prelude does not decide hook order. The order is resolved when the initramfs is built, by mkirf, and recorded in the image. prelude reads the resolved list and runs it; it has no ordering logic of its own.
- prelude does not supervise anything. It runs the hook sequence once and execs the real init. It has no steady state — contrast peinit, which runs as the system's lifecycle manager for the whole of its uptime.
- prelude is not the real init. It is the initramfs init. peinit is the real-root init. They are separate binaries with separate jobs; prelude's last act is to exec the real init, not to become it.
- prelude does not persist anything. Nothing it does is written to disk. The initramfs is in RAM, used once, and discarded. Persistent state begins on the real root, with peinit.
Where to go next #
For the deployment-specific scripts prelude runs, read Boot hooks.
For the init system prelude hands the machine to, read peinit at PID 1.
For the tool that compiles /boot/initramfs/ into the image, read mkirf.
peinit at PID 1
Peios / Peios Security Fundamentals / Boot and trust establishment
peinit is the init system of a running Peios system — the process that takes over once the initramfs stage has mounted the real root. It runs at PID 1, signed at TCB level, holding the SYSTEM token. From there, peinit does the rest of the work needed to bring the system from a freshly-mounted real root to "fully-running with services and users".
The specific responsibilities are:
- Run the compiled-in Phase 1 bootstrap: probe that the root is writable, mount the virtual filesystems, restore the random seed, ensure the machine-id, set the clock from the RTC, and start registryd.
- Read the service catalog from the registry (
Machine\System\Services\), validate the dependency graph, and start services in dependency order — authd among them. - Mint SYSTEM tokens for platform services itself; request every other identity from authd.
- Continue running as the system's lifecycle manager — handling service crashes, system shutdown, eventual reboot.
This page covers each of these responsibilities, why peinit specifically is the right thing to be PID 1, and the patterns peinit uses for the work.
Why peinit at PID 1 #
PID 1 has special properties in Linux: it cannot be killed by ordinary signals, it inherits orphaned processes, it's the ancestor of everything else. Whichever process is PID 1 is operationally critical.
Peios chooses peinit specifically because:
- It's signed at TCB level. When peinit is exec'd — by prelude, at the switch to the real root — the signature verification at exec sets
pip_type = Protected,pip_trust = 8192. peinit is now PIP-protected at the highest level. No other process can signal it, debug it, or interfere with it. - It runs on the SYSTEM token. Inherited from init. peinit has every privilege; it can do anything that requires authority.
- It's purpose-built for the bootstrap. Other init-style processes do general-purpose process management; peinit specifically knows about Peios's identity model, its registry-defined service catalog, and the service-launching pattern Peios uses.
The combination of "PIP-protected" and "all privileges" and "purpose-built" makes peinit the right thing to be PID 1. A general-purpose init that wasn't signed at TCB level would be a weak link — every TCB-level process (authd, loregd, eventd) is unreachable by their lifecycle manager, which means lifecycle management could only be done by another TCB-level process. That role is peinit.
The TCB lifecycle-manager pattern #
A consequence of PIP: only a process that PIP-dominates the TCB daemons can signal them. Since the TCB daemons are at pip_type = Protected, pip_trust = 8192, only another process at the same level (or higher, but there is no higher in v0.20) can dominate.
peinit is the only such process at the right time. peinit is signed at TCB level, so it dominates. authd, loregd, eventd, lpsd are also at TCB level, so they dominate each other — but they don't manage each other's lifecycles by convention. peinit is the conventional lifecycle manager.
This is documented in PIP in practice. The TCB-lifecycle-management pattern is a consequence of PIP; peinit happens to be the binary that fills the role.
What peinit does at startup #
peinit's startup is two phases. The authoritative, step-by-step account lives in the peinit topic at Boot and boot modes; what follows is the trust-chain summary.
Phase 1 — compiled-in bootstrap #
Phase 1 runs before any configuration is available, in a fixed order compiled into the binary:
- Assert PID 1. peinit refuses to run as anything else.
- Probe that the real root is writable, and ensure the virtual filesystems (
/proc,/sys,/dev, …) are mounted. - Restore the random seed, ensure the machine-id, set the clock from the RTC.
- Start registryd and probe the registry schema. The registry is the source of all subsequent configuration, so its daemon comes up before anything else.
- Provision boot paths and infrastructure — the control socket, the job filesystem, loopback networking.
Phase 2 — the service graph #
With the registry available, peinit reads the service catalog from Machine\System\Services\, validates the dependency graph, and starts services in dependency order. authd is an ordinary Critical service in this graph — started after the infrastructure it depends on (eudev, lpsd), gated by the same readiness mechanics as any other service, not by a special wait-for-authd phase.
For each service, the pattern is fork-install-exec:
- peinit obtains the service's token — minted by peinit itself for SYSTEM/platform services, requested from authd for every other identity (see Identity and privileges).
- peinit forks; the child inherits a SYSTEM-derived token.
- peinit installs the service-specific token on the child via
KACS_IOC_INSTALL. - The child execs the service binary. The kernel verifies the signature and sets the PIP fields.
Service mitigation limits #
In the source-checked peinit 0.0.12, commit 0e20fef, the
service-field catalog
has no per-service mitigation-policy field. The child dispatch
and child setup through exec
install the prepared token, set up the working directory and file descriptors,
and execute the service without an extra PSB mitigation request.
This reviewed launch path therefore does not apply an additional per-service mitigation mask. It does not establish that the resulting process has no mitigations: inherited flags and application self-hardening are separate. Inspect committed mitigations on the actual process and re-check replacements. This source finding does not verify the deployed release, kernel activation behavior or a running service's committed state.
Steady state #
After the graph is started, peinit transitions to its steady-state role: the lifecycle manager. It watches for service crashes (a child process dies; peinit receives SIGCHLD), restarts them per policy, handles shutdown signals (an administrator calling shutdown), and otherwise runs as a long-lived daemon.
The system is now "up" — services are running, authd is creating tokens for users who sign in, FACS and KACS are enforcing access control, audit events are flowing through KMES.
The fork-install-exec pattern #
The pattern is the most important thing peinit does, and it's worth pinning. The order of operations is deliberate:
- fork. Creates the child process. The child inherits the parent's token, file descriptors, and PSB. At this point the child is essentially a clone of peinit.
- Install token. The child gets its service-specific token via
KACS_IOC_INSTALL. This is the moment its identity is set to be the right thing for the service. - exec. The new program runs. The kernel verifies the binary's signature and sets the PIP fields.
The reason for this order: the service's token needs to be in place before the service's startup code runs — the service should be able to assume from its first instruction that it's running as the right identity. exec comes last because that is the moment the new program takes over; by then the identity has been decided.
The pattern is what every service is launched through. Variations are minor — the specific token differs per service — but the structural sequence is the same.
What peinit does not do #
A few clarifications:
- peinit mints only SYSTEM tokens. Minting the SYSTEM token for platform services via
kacs_create_tokenis peinit's normal, specified path — no authd interaction is needed for those. Every other identity is requested from authd, the source of truth for who-gets-what privileges. - peinit is not the only TCB process. authd, loregd, eventd, lpsd are also at TCB level. They have their own jobs.
- peinit does not enforce policy. DACLs, conditional ACEs, mount policies — the kernel runs AccessCheck and makes every access decision; peinit just launches processes with the right identities.
- peinit does not handle user sessions directly. Users sign in via authd; authd produces tokens; peinit launches the session's first process. The user's session is managed by authd, not peinit.
- peinit's service management is peinit, not a separate daemon. Start order, dependencies, restart policy, the service state machine, and the control interface are peinit's own responsibility — there is no service-manager process living alongside it. That model is substantial enough to have its own topic; this page does not cover it. See peinit.
When peinit fails #
If peinit crashes or exits, the kernel typically panics — losing PID 1 is a system-fatal condition in Linux. Peios's peinit is built to be very stable for this reason; its work is narrow and bounded.
If peinit cannot complete a startup step (a service won't launch, authd won't start), the deployment-specific behaviour depends on the configured policy. Typical responses:
- For a critical failure (authd won't start), boot fails. The system enters a recovery state — typically a console login as SYSTEM, with limited services running, enough for an administrator to diagnose and fix.
- For a non-critical failure (a single service won't start), peinit logs the failure, leaves the service unstarted, and continues with the rest of boot.
The failure-mode behaviour is configurable but conservative by default. Successful service launch is not evidence that peinit applied an additional mitigation policy: the reviewed launch path has no such setting. Inspect the running process's committed protection instead.
authd handoff
Peios / Peios Security Fundamentals / Boot and trust establishment
The single most consequential transition during boot is the moment authd comes online. Before authd, every process is running on the SYSTEM token (inherited from init through peinit); identity is uniform and authority-maximal. After authd, processes start running as their actual principals — services as their service identities, users as themselves after they sign in. authd is the bridge between "kernel-direct bootstrap identity" and "real identities derived from the directory".
This page covers what authd does at startup, how the handoff from SYSTEM-everywhere to real-identities-everywhere happens, and what authd is responsible for in steady state.
What authd is #
authd is the authentication daemon. Its core responsibilities:
- Authenticate principals. When a user signs in, authd verifies their credentials against the directory (locally for standalone systems, against the domain's directory for domain-joined ones).
- Mint tokens. Once a principal is authenticated, authd produces a token reflecting their identity, group memberships, privileges, integrity level, claims. The token is what the principal's processes run on.
- Manage logon sessions. authd creates a session per authentication event and attaches the minted tokens to it. The kernel destroys the session when its last token goes; authd keeps no per-session state, so it has nothing to clean up then. On request, it ends a session early by ending its processes.
- CAAP distribution is not implemented. authd does not load, update or replicate the kernel's central access policies. See Distribution and recovery.
- Resolve identity-related queries. authd is the answer to "what privileges does this user have?", "what claims should be on this token?", "is this user a member of this group?". These queries go to authd, which consults the directory.
authd is signed at TCB level, runs at pip_type = Protected, pip_trust = 8192, and is one of the PIP-protected processes. It is launched by peinit early in boot and runs continuously until shutdown.
authd's startup sequence #
When peinit forks-and-execs authd, the new process goes through several initialisation steps:
1. Verify own state #
Just like peinit at startup, authd verifies it is running in the expected state — PIP-protected, holding the expected token, with the expected mitigations applied. Anything else indicates the boot is broken; authd refuses to proceed.
2. Connect to the directory #
authd opens its connection to the directory. The connection mechanism depends on deployment:
- Standalone systems use loregd (the registry daemon). authd communicates with loregd over a Unix socket and reads policy from registry keys.
- Domain-joined systems use Samba 4. authd talks to a Samba 4 instance (locally or on another machine in the domain) for domain identity and policy resolution.
The directory is the source of truth for everything authd needs to know — principals, groups, privileges, claims. authd's connection to the directory is established early because everything else depends on it.
If the directory connection fails (loregd not running, AD unreachable), authd cannot mint real tokens. The system continues on bootstrap tokens (everything as SYSTEM) until the directory becomes available. authd retries with exponential backoff; in the meantime peinit may proceed with whatever services don't need authd.
3. Populate the CAAP cache #
This step is not implemented. authd does not call kacs_set_caap or
load central access policies from a registry or directory. The kernel cache
starts empty; references to missing policies use the recovery policy.
authd's readiness signal does not certify policy loading. See
Distribution and recovery.
4. Begin serving authentication requests #
authd opens its socket — typically a Unix domain socket — and starts accepting connections from clients. The clients include:
- peinit, asking for tokens to assign to new services.
- Login frontends (console, SSH, terminal services) authenticating users.
- Services that handle their own re-authentication (rare, but possible).
Each authentication request follows a similar pattern:
- Client connects to authd's socket and presents credentials.
- authd verifies the credentials against the directory.
- authd resolves the principal's full identity (groups, privileges, claims) from the directory.
- authd creates a logon session via
kacs_create_logon_session. - authd calls
kacs_create_tokento mint the token, passing the wire-format specification with everything resolved. - authd returns the token (or token fd) to the client.
The client now has a token reflecting the authenticated principal. The client can install the token on a child process (if it's peinit launching a service) or on itself (if it's a login frontend completing the user's sign-in).
5. Signal readiness #
authd writes a readiness signal to peinit — typically by closing a pre-existing pipe fd, by writing a sentinel value to a known location, or by reaching a state peinit can probe for. peinit, which has been blocked waiting for this, proceeds to launch the rest of the services.
The handoff in one paragraph #
The transition: before authd is ready, every userspace process is running on the SYSTEM token (or a SYSTEM-derived FilterToken variant). After authd is ready, peinit's subsequent service-launch operations assign each new service a token authd produces — derived from the directory, with the appropriate identity for the service. The system goes from "everything SYSTEM" to "services as themselves" gradually, over the course of however long peinit takes to launch the rest of userspace.
There is no single moment where the system "becomes secure" — it's a gradient. Even before authd, the kernel's access control is fully operational; SYSTEM just happens to be everywhere. As real tokens replace SYSTEM in process after process, the system's authorisation surface narrows. By the time login frontends are accepting users, every service is at its right identity, and users sign in to fresh sessions of their own.
authd in steady state #
After startup, authd's day-to-day work is:
- Authenticate users when they sign in. Each login produces a session and a token (or a pair of tokens for UAC-style elevation).
- Mint tokens for new service starts. When peinit needs a token for a service it's about to launch, authd produces one.
- End sessions on request. When a person signs themselves out everywhere, or an administrator signs somebody out, a program asks authd over
/run/logon.sock. authd checks the caller may, finds every process whose primary token belongs to the session, and sends eachSIGTERM, thenSIGKILLafter a grace period, looking again for anything forked meanwhile. The kernel destroys the session when the last token goes. See Session lifecycle and PGSS §2.22.
authd is a long-running daemon. It is not periodically restarted. Its uptime equals the system's uptime (modulo any administrative restarts in response to configuration changes).
What authd does not do #
A few clarifications:
- authd does not make access decisions. AccessCheck is in the kernel. authd produces inputs to it (tokens) but does not decide whether any specific access is granted.
- authd does not enforce or distribute CAAP. AccessCheck enforces policies installed in the kernel cache. Automatic distribution is not implemented.
- authd does not authenticate every operation. Authentication is a sign-in event. Operations done after sign-in run against the token, not against authd. authd does not see most of the per-operation traffic.
- authd does not store passwords. Credential verification goes against the directory; authd is an intermediary. The directory (loregd's registry, or the domain's directory) is where the credentials live.
- authd does not implement the directory. authd consumes a directory; it doesn't be one. For standalone systems, loregd is the directory; for domain-joined, the domain's directory via Samba 4. authd talks to whichever is appropriate.
The cleanest mental model: authd is the converter between "directory state" and "kernel-resolvable identity". Everything else — access decisions, audit, lifecycle — happens around authd, not through it.
When authd fails #
authd is signed at TCB level, runs hardened, and is operationally critical. If authd crashes:
- New authentication requests fail. Users cannot sign in, peinit cannot get new tokens for new services.
- Existing tokens continue to be valid. Processes running on tokens authd already minted are unaffected.
- Any CAAP entries explicitly installed in the kernel cache remain there. authd does not populate or update that cache.
peinit detects authd's exit (it's a child of peinit) and typically restarts it per policy. The restart goes through the same fork-install-exec pattern as the initial launch; restarting authd does not populate the CAAP cache.
Sessions that authd had been managing are still associated with the old authd's auth_id values; the new authd doesn't "inherit" them but doesn't need to — the existing sessions continue running on the tokens already minted; the kernel knows they exist.
A failure that exceeds the restart policy's retry budget is treated as a critical failure; peinit may transition the system to a recovery state where administrative intervention is required.
Boot ordering implications #
Knowing what authd does has implications for boot ordering:
- Services that need CAAP cannot use authd readiness as proof that policies are loaded. Automatic distribution and a policy-readiness barrier are not implemented.
- Services that need real tokens must start after authd is up. Similarly waited for.
- Services that are happy with SYSTEM can start before authd. This is mostly registry, observability, and other infrastructure services.
- Login frontends must start after authd is up. They can't authenticate users otherwise.
The exact graph of dependencies is part of peinit's configuration. peinit knows which services depend on what and starts them in the right order.
The handoff is one-way #
A subtle but important property: the handoff from kernel-direct tokens to authd-managed tokens is one-way. Once authd is running and has taken over identity creation, the system does not revert to "kernel-direct only" without a full restart.
If authd crashes and restarts, it resumes its authentication responsibilities. It does not populate CAAP. It doesn't reset the system to SYSTEM-everywhere; that initial transition happens only once per boot.
The reason: the running services have already been assigned real tokens. Going back to SYSTEM would require reassigning every service's token, which would mean restarting every service. That's a system-wide reboot, not a recovery.
So the trust establishment chain is a one-time event per boot. It happens early; it sets up the system's authority distribution; it stays in place until shutdown. Reboot is the only way to redo it.
Where to go next #
For the init system that launches authd and waits on its readiness, read peinit at PID 1.
For the SYSTEM-everywhere state the handoff replaces, read Bootstrap tokens.
For the recovery policy that applies until the CAAP cache is populated, read Distribution and recovery.