On this page
- §11.1.1 Control socket
- §11.1.1.1 Wire format
- §11.1.1.2 Peer authentication
- §11.1.1.3 Limits
- §11.1.2 Notify socket
- §11.1.2.1 Sender authentication
- §11.1.3 Fd store
- §11.1.3.1 Schema control
- §11.1.3.2 Storing an fd
- §11.1.3.3 Removing an fd
- §11.1.3.4 Injection on restart
- §11.1.3.5 Clearing the fd store
- §11.1.4 Outbound IPC
Protocol
§11.1.1 Control socket
peinit exposes a Unix stream socket at /run/peinit/control.sock
for runtime commands. The socket is created during Phase 1
infrastructure setup (after /run is mounted and registryd is
running) and exists for the lifetime of the system.
§11.1.1.1 Wire format
Messages are newline-delimited JSON. Each message is one JSON object per line -- no pretty-printing. This eliminates ambiguity about message boundaries.
Request format:
{"command": "start", "service": "jellyfin", "wait": true}
Success response:
{"status": "ok", "operation_id": "a1b2c3d4-...", "service": "jellyfin", "state": "active", "cause": "explicit_start", "warnings": []}
Error response:
{"status": "error", "code": "ACCESS_DENIED", "message": "caller lacks SERVICE_START on jellyfin"}
Field names, types, and value formats in JSON examples throughout this specification are normative.
§11.1.1.2 Peer authentication
When a client connects, peinit MUST obtain the caller's identity
via kacs_open_peer_token (KACS syscall). This returns a token fd
representing the peer's KACS identity. The kernel provides this --
there is no userspace credential exchange.
The token returned by kacs_open_peer_token is the peer thread's
effective token at connection time -- if the peer is impersonating,
the impersonation token is captured. This ensures that access
control decisions reflect the identity under which the client is
actually operating, not its underlying service identity. The exact
semantics of kacs_open_peer_token are defined in the KACS v0.20
specification.
For each command, peinit MUST call AccessCheck with:
- Token: the peer's token.
- SD: the target service's ServiceSecurity SD (for service commands) or peinit's control SD (for system commands).
- Desired access: the access right for the command.
If AccessCheck denies access, peinit MUST return an ACCESS_DENIED error and log the attempt.
§11.1.1.3 Limits
peinit MUST enforce hard limits on the control socket:
| Registry key | Default | Description |
|---|---|---|
Machine\System\Init\MaxControlConnections |
32 | Maximum concurrent client connections. |
Machine\System\Init\MaxRequestSize |
65536 | Maximum request size in bytes. |
Machine\System\Init\ConnectionTimeout |
30 | Seconds before an idle connection is closed. |
Connections exceeding MaxControlConnections MUST be rejected. Requests exceeding MaxRequestSize MUST be rejected. Idle connections exceeding ConnectionTimeout MUST be closed.
§11.1.2 Notify socket
peinit creates a Unix datagram socket for sd_notify messages. The
path is an internal implementation detail -- services receive it
via the NOTIFY_SOCKET environment variable set during pre-exec.
No service hardcodes the path.
§11.1.2.1 Sender authentication
peinit MUST authenticate sd_notify senders:
- Enable
SO_PASSCREDon the notify socket. - Receive the sender's PID via
SCM_CREDENTIALS(kernel-attested). - Validate the PID against the service's tracked main PID (verified via pidfd). NotifyAccess=Main is the only supported mode.
- Validate the start generation -- messages from a previous incarnation MUST be rejected.
Messages from unrecognised senders MUST be dropped and logged.
UID/GID from SCM_CREDENTIALS are not policy inputs.
§11.1.3 Fd store
The fd store allows a service to persist file descriptors across restarts. A service pushes fds to peinit via sd_notify; peinit holds them and re-injects them into the new process on restart. This enables stateful daemons (e.g., a web server preserving listening sockets) to restart without dropping connections.
§11.1.3.1 Schema control
The FdStoreMax field in the service definition (default 0) controls the maximum number of file descriptors peinit will hold for the service. When FdStoreMax is 0, the fd store is disabled -- FDSTORE=1 messages are logged and the accompanying fd is closed.
§11.1.3.2 Storing an fd
When a service sends an sd_notify message containing FDSTORE=1
with a file descriptor attached via SCM_RIGHTS:
- peinit MUST authenticate the sender using the same PID-matching and generation validation as all other sd_notify messages (see Sender authentication above).
- If the service's FdStoreMax is 0, peinit MUST log the rejection and close the received fd.
- If the fd store already contains FdStoreMax entries, peinit MUST log the rejection and close the received fd. The existing store is not modified.
- If
FDNAME=<name>is present in the message, the fd is stored under that name. If FDNAME is absent, the fd is stored under the default name"stored". - If
FDPOLL=0is present, peinit MUST mark the fd as exempt from poll monitoring. By default, peinit MAY monitor stored fds for error conditions (POLLERR, POLLHUP) and remove fds that become invalid.
Multiple fds MAY share the same name. On injection, all fds with a given name appear in LISTEN_FDNAMES in storage order.
§11.1.3.3 Removing an fd
When a service sends an sd_notify message containing
FDSTOREREMOVE=1 with FDNAME=<name>:
- peinit MUST authenticate the sender.
- peinit MUST remove all stored fds matching the given name and close them.
- If no fd with the given name exists, the message is a no-op. peinit MUST NOT treat this as an error.
§11.1.3.4 Injection on restart
When a service restarts (transitions from a failed or stopping state back to Starting), peinit MUST inject stored fds into the new process during child pre-exec (Step 8 of the Pre-Exec Sequence):
- Stored fds are passed as inherited file descriptors starting at
SD_LISTEN_FDS_START(file descriptor 3). - The
LISTEN_FDSenvironment variable MUST be set to the number of injected fds. - The
LISTEN_FDNAMESenvironment variable MUST be set to a colon-separated list of fd names, in the same order as the fd numbers. - After successful injection, the fd store is cleared -- peinit no longer holds the fds.
§11.1.3.5 Clearing the fd store
The fd store MUST be cleared (all stored fds closed) in the following situations:
- Explicit stop: when a service is stopped by an administrator or by shutdown (not a crash-triggered restart). The fds are no longer useful -- the service is not coming back.
- Service removal: when a service definition is removed from the registry. All associated state, including stored fds, is discarded.
The fd store survives across automatic restarts (crash -> restart policy -> new start). This is the entire point of the mechanism -- fds persist through the restart that the service cannot control.
§11.1.4 Outbound IPC
peinit connects to three services:
| Service | Purpose | Protocol | Failure behaviour |
|---|---|---|---|
| registryd | Service definitions, mount config, boot settings. | LCS syscalls (boot); in-memory cache + change notifications (runtime). | Phase 1: recovery mode. Runtime: operates on cached model. |
| authd | Service tokens. | JSON over Unix stream socket (non-blocking, state-machine driven with timeout). | Non-SYSTEM services cannot start. Platform services unaffected. |
| eventd | Forward logs, drain pre-eventd ring buffer. | JSON over Unix stream socket (non-blocking). | Logs accumulate in pre-eventd ring buffer. Buffer is bounded. |
All outbound IPC MUST be non-blocking. authd and eventd use Unix stream sockets with epoll. Token requests to authd are state- machine driven with timeouts -- if authd is unresponsive, the service start fails rather than PID 1 hanging.