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

Operation Model

An operation is a first-class object representing a requested state machine action on a service. Instead of control commands directly mutating service state, every command creates an operation that is validated, queued, and executed by peinit's event loop.

Operations exist because peinit handles concurrent callers (admin tools, remote SCM, PAC, automated triggers). Without operations, concurrent commands collide with implementation-defined behaviour. With operations, conflict resolution is explicit and observable.

§8.1.1 Lifecycle

Pending --> Running --> Completed
  |                      |
  +--> Merged            +--> Failed
  |                      |
  +--> Cancelled         +--> Aborted
State Meaning
Pending Validated and queued. Waiting for preconditions (e.g., a prior stop to complete before a queued start can run).
Running Actively executing. For start: the service is transitioning through Starting. For stop: the service is in Stopping.
Completed The operation achieved its goal. Start: service reached Active/Completed. Stop: service reached Inactive or Failed (the service is no longer running). Reload: reload acknowledged.
Failed The operation did not achieve its goal. Start: service reached Failed. Stop: timed out (service did not exit within StopTimeout + SIGKILL escalation).
Merged This operation was merged into an existing identical operation. The merged-into GUID is recorded.
Cancelled Terminated while Pending -- the operation never executed.
Aborted Terminated while Running -- the operation was in progress and was interrupted.

Cancelled and Aborted are the same concept (terminated before completion) at different lifecycle points. Cancelled = never ran. Aborted = was running. The cause (admin action, conflict supersession) is a property of the event, not the state.

§8.1.2 Fields

Operation {
    id:           GUID        // unique identifier
    type:         enum        // Start, Stop, Restart, Reload, Reset
    service:      string      // target service name
    state:        enum        // Pending, Running, Completed, Failed,
                              // Merged, Cancelled, Aborted
    created_at:   timestamp   // when the operation was created
    started_at:   timestamp?  // when execution began
    completed_at: timestamp?  // when terminal state was reached
    source:       enum        // Admin, DependencyPropagation,
                              // RestartPolicy, Timer,
                              // BindsToRecovery, ConflictResolution
    caller:       token_summary?  // caller identity (admin-initiated)
    result:       string?     // human-readable result or error
    merged_into:  GUID?       // if Merged, the target operation
}

§8.1.3 Operation types

§8.1.3.1 Start

Creates a job for the target service. If the service has unsatisfied dependencies, start operations are created for each dependency (source: DependencyPropagation). The operation completes when the service reaches Active (Simple) or Completed/Inactive (Oneshot).

§8.1.3.2 Stop

Sends SIGTERM, starts StopTimeout, escalates to SIGKILL. The operation completes when the service reaches Inactive (clean stop) or Failed (conflict eviction, BindsTo propagation).

§8.1.3.3 Restart

A Stop followed by a Start. Implemented as a single operation -- peinit stops the service then starts it, tracking both phases under one operation GUID. If the service has no running process (Inactive, Completed, Failed, Skipped), the stop phase is skipped and peinit proceeds directly to the start phase. The operation type remains Restart for observability. The operation completes when the service reaches Active after the restart.

§8.1.3.4 Reload

Sends the ExecReload command or signal (see the Restart and Reload section). The operation completes when the reload resolves. Unlike other operations, reload defaults to wait=false -- the caller gets the operation GUID immediately.

§8.1.3.5 Reset

Clears Failed, Abandoned, or Skipped state. Transitions the service to Inactive. Synchronous -- completes immediately.

§8.1.4 Timeout

Operations inherit their target service's StartTimeout or StopTimeout as a maximum lifetime. The timeout clock starts at operation creation (including queue time), not at execution. From the caller's perspective, they have been waiting since they sent the command.

§8.1.5 Retention

peinit retains operation objects in memory while they are Pending or Running. Terminal operations (Completed, Failed, Cancelled, Merged, Aborted) are emitted to eventd as structured events and dropped from memory after a grace period (default 60 seconds -- long enough for a polling client to retrieve the result).

peinit MUST NOT maintain operation history. eventd is the historian.

§8.1.6 eventd integration

peinit MUST emit a structured event at each operation lifecycle transition:

  • operation.requested -- operation created. Includes operation_id, type, service, source, caller.
  • operation.started -- execution began (Pending -> Running).
  • operation.completed -- succeeded. Includes duration, result.
  • operation.failed -- did not achieve its goal. Includes failure reason, duration.
  • operation.cancelled -- superseded or explicitly cancelled.
  • operation.merged -- merged into an existing operation. Includes merged_into GUID.
  • operation.aborted -- was running, interrupted.

§8.1.7 Control protocol integration

Every control command returns an operation GUID:

{"status": "ok", "operation_id": "a1b2c3d4-..."}

Callers can poll for completion via the operation-status command:

{"command": "operation-status", "operation_id": "a1b2c3d4-..."}

With wait=true, the control socket connection stays open until the operation reaches a terminal state.