8.4 Event Emission

peinit emits a structured event at every job and operation lifecycle transition, and for its own audit records. All of them go into the KMES kernel ring buffer through kmes_emit and kmes_emit_batch, encoded as msgpack per the KMES event-record format (Peios Kernel TRM §2).

There is no event socket. Structured events are not sent to eventd over any connection. eventd consumes them from the ring buffer, which is why they survive eventd being down, being restarted, or not existing yet. The only thing peinit sends eventd over a socket is service output (§11.4), which is a different path with different guarantees.

8.4.1 Job events #

job.created — the job object exists. Carries the job identifier, service name, type, image path, identity and operation identifier.

job.started — exec succeeded. Carries the job identifier, PID and cgroup path.

job.ended — the process exited or was killed. Carries the job identifier, final state, exit code or signal, duration and failure cause.

The event type is the dotted string; the fields form the msgpack payload. The payloads are supersets of the summaries above — job.ended in particular carries the whole record.

8.4.2 Operation events #

Every operation event carries: operation_id, type, service, source, caller — null for a lifecycle-generated operation — and state after the transition.

EventAdds
operation.requested
operation.started
operation.completedduration_ns, result
operation.failedduration_ns, failure_reason
operation.cancelledreason
operation.mergedmerged_into
operation.abortedduration_ns, reason

duration_ns is measured from creation, not from the start of execution — the same reasoning as the operation timeout. What a caller waited is what matters, and queue time is part of it.

The same field appears under three names across the two surfaces: failure_reason on operation.failed, reason on operation.cancelled and operation.aborted, and error in the control interface's operation view (PSPU §4).

8.4.3 Ordering #

When one runtime step produces several lifecycle events, peinit emits them in causal order before committing the retained state for that step. For a terminal pre-start graph dispatch, the terminal event for the operation whose outcome satisfied or failed the graph input precedes the events for the operations that dispatch releases, which preserve graph dispatch order.

Every operation in a graph context is requested when the context is built rather than when its turn comes, so what a release emits is operation.started.

8.4.4 Audit and graph events #

peinit's own audit records go through the same path: access.denied for a refused control command, with the caller's SID, the target, the requested right by name and the access bits requested and granted; on_failure.loop_suppressed when the fallback chain guard trips; graph.validation_error and graph.validation_warning for validation findings; notify.rejected for an unauthenticated notification; fd_store.rejected for a refused descriptor; notify.status, notify.errno and notify.exit_status for the three event-emitting notification fields; cgroup.leaked the first time a sub-cgroup is found still populated after its post-kill deadline (§5.7); and graph.operation_terminal for a graph member's terminal outcome.

Audit records are events rather than logs, and that distinction is the point: the ring buffer persists from the moment PKM loads, so an access denial during Phase 1 is captured before the registry exists, let alone eventd.

Edit this page