5.5 The Base Environment

peinit constructs every service and hook process's environment from scratch, in four layers, lowest precedence first. Nothing is inherited : peinit's own startup environment holds TERM and nothing else (§2.1), and none of it is passed through.

5.5.1 Layer 1: the compiled-in base #

One variable:

VariableValue
PATH/sbin:/bin

Executables are addressed through the root-level StrataFS runtime views. Package storage paths under /usr are deliberately not on the default search path.

5.5.2 Layer 2: global environment variables #

Each value under Machine\System\Init\EnvVars\ becomes a variable: the value name is the variable name, the REG_SZ data is the value. An EnvVars\PATH overrides the compiled-in PATH; every other name adds.

A malformed entry — an empty name, or a name containing = — fails the whole layer, which at boot means recovery mode.

registryd does not receive this layer. The exemption is a trust rule, not an availability one. Write access to EnvVars\ is equivalent to compromising every service peinit starts: LD_PRELOAD, LD_LIBRARY_PATH and their relatives are not filtered, because the key's Security Descriptor is meant to be the control boundary. A key that could inject into the daemon that enforces who may write it would make that boundary self-referential.

The exemption is narrow, and matches on two things at once: the job's resolved identity is SYSTEM and its service name is registryd. A non-platform service that happens to be called registryd receives the ordinary layering. It matches on the job, so a hook of registryd's running under a non-SYSTEM HookIdentity would receive the layer; and it matches the resolved identity string, so a definition naming S-1-5-18 literally rather than SYSTEM would not be exempt.

registryd is launched in Phase 1, before EnvVars has been read at all, so the exemption is only observable on a restart.

5.5.3 Layer 3: the service's own Environment #

The definition's Environment entries, overriding both layers below.

5.5.4 Layer 4: protocol variables #

NOTIFY_SOCKET, always. LISTEN_FDS, LISTEN_FDNAMES and LISTEN_PID, only when descriptors are being injected — from the fd store for a service, or from a submission's attachments for a submitted job (§8.5).

These have the highest precedence. The first three are inserted after both configurable layers, and all four names are dropped from those layers before insertion, so a service cannot override NOTIFY_SOCKET and break its own notification protocol, and an EnvVars\LISTEN_FDS=3 cannot point an fd-store-less service's sd_listen_fds at whatever happens to sit at descriptor 3. The name filter is what protects the LISTEN_* set when it is not being set.

LISTEN_PID, which a conforming sd_listen_fds implementation checks against its own PID before trusting LISTEN_FDS, is appended by the child itself after the clone, because only then is the PID known; the name filter keeps a configured value from shadowing it.

5.5.5 What peinit does not set #

Not HOME, USER, LOGNAME, SHELL or TERM. Peios identity is a KACS token — a SID — rather than a passwd entry, so there is no canonical home directory or login shell to populate. A service that needs one supplies it through EnvVars\ or its own Environment; a submitted job's submitter supplies it in the submission's environment, which occupies layer 3 for a job.

5.5.6 Hooks and probes #

Hooks, health checks and reload commands are built through the same path, so they receive the identical environment: the same layers, the same NOTIFY_SOCKET, and the service's WorkingDirectory, LimitNOFILE, LimitCORE and RequiredPrivileges. They never receive LISTEN_FDS — stored descriptors go to the main process only.

5.5.7 When changes apply #

The global layer is a snapshot refreshed at boot and on reload-config, and both it and the per-service Environment take effect at a service's next start. Neither is applied to a running process.

Edit this page