These docs are under active development and cover the v0.20 Kobicha security model.
On this page
§3.1

Overview

A service is the primary unit of management in peinit. Every supervised process -- a long-running daemon, a run-to-completion task, a mount operation -- is represented as a service with a definition, a runtime state, and a security policy.

Service definitions are stored in the registry under Machine\System\Services\<name>. The key name is the service name. peinit reads definitions at Phase 2 boot and on demand when an administrator starts a service or issues a reload-config command.

§3.1.1 Service types

peinit supports two service types.

§3.1.1.1 Simple

A long-running daemon. peinit forks, installs a token, and execs the binary. The process IS the service. When it exits, the service has stopped. This is the default type and covers the majority of services (registryd, authd, sshd, application services, etc.).

Readiness: determined by the Readiness field. Notify (default) requires the service to send READY=1 via sd_notify. Alive treats the process as ready as soon as it exists.

Lifecycle: the service transitions to Active on readiness and remains Active until the process exits or is stopped.

§3.1.1.2 Oneshot

A run-to-completion task. peinit forks, installs a token, execs the binary, and waits for it to exit. Success (exit code 0, or any code in SuccessExitCodes) marks the service as completed; failure triggers the failure policy. Used for setup tasks (database initialisation, directory creation, schema migration).

Readiness: the Readiness field is ignored. Oneshot readiness is always "successful exit." A oneshot that exits 0 has succeeded; one that exits non-zero has failed. READY=1 is meaningless for a process whose job is to exit.

Lifecycle differences from Simple:

  • Successful exit transitions to Completed. With RemainAfterExit=1, the service remains in Completed. Without RemainAfterExit, the service transitions Completed -> Inactive after dependents are released.
  • Non-zero exit transitions to Failed.
  • ExecStartPost runs after successful exit, not after a readiness signal. If the oneshot fails, ExecStartPost MUST NOT run.
  • StartTimeout covers the entire execution time -- from first pre-hook to process exit.

RemainAfterExit: a Oneshot service with RemainAfterExit=1 stays in the Completed state after its process exits successfully. Without RemainAfterExit, the service passes through Completed (releasing dependents) then transitions to Inactive. RemainAfterExit is useful when the Completed state must persist -- for example, so status queries show the service as finished rather than inactive.

§3.1.2 Triggers

Triggers determine when a service starts. A service's type (Simple/Oneshot) is independent of its triggers. Triggers are stored as a multi_string array on the service definition, each entry in the format type or type:argument.

Trigger Format Meaning
Boot boot Start during the Phase 2 boot sequence.
Timer timer:<schedule> Start on a schedule. The schedule is a calendar expression (see the Timers section).

A service with no triggers is demand-only -- it is started exclusively via explicit control interface commands.

Multiple triggers of the same type are permitted. A service with ["timer:*-*-* 02:00:00", "timer:*-*-* 14:00:00"] runs at 2am and 2pm. The shortcuts daily, weekly, hourly, and monthly are supported.

The trigger model is designed to be extensible. Future trigger types (path, device, event) slot into the same array without schema changes.

§3.1.3 Disabled flag

A service with Disabled=1 MUST NOT be started by any trigger (boot, timer, or future trigger types). The Disabled flag suppresses automatic activation only -- the service MAY still be started explicitly via the control interface.

To prevent a service from being started entirely, modify its ServiceSecurity SD to deny SERVICE_START. This is an access control concern, not a trigger concern.

Enable and disable are registry write operations performed by admin tools, not peinit control commands. peinit picks up Disabled flag changes via registry change notifications or on reload-config.

§3.1.4 Forking daemons

peinit does not support forking daemons. Services that double-fork to "daemonise" themselves are using a workaround for a problem that does not exist when a service manager tracks the process it spawned. peinit tracks the process it forks via pidfd.

If a legacy binary requires double-fork behaviour, the role packager MUST wrap it (e.g., a wrapper script with a --no-daemon flag). This is a packaging concern, not an init concern.