Service
service manages whether a service is running, enabled, and masked, and how it reacts to notifications. Systemd is the built-in backend; plugins can add others. This example names systemd explicitly:
Null optional fields follow omission and default behavior. stopped means inactive; it does not delete, disable, or mask the unit. There is no service absent state.
Systemd convergence
Unmasking precedes enable/start; stop/disable precede masking. A masked unit needs masked = false before it can be started or enabled. Enabling static, generated, or transient units fails.
Missing, malformed, transitional, and unsupported states fail instead of being treated as absent. Canonical unit identities detect alias ownership conflicts, including a fresh check after definition changes. Default budget: two minutes. Systemd requires service.systemd.v1; other backends declare their required capabilities.
Refresh policy
reload is strict: failed or unsupported reload does not silently become a restart. Choose reload_or_restart if you want that fallback. none consumes notification work without a service mutation.
An inactive or explicitly stopped unit consumes refreshes without starting. A verified initial start consumes pending config refreshes without a redundant restart. Refresh is separate from ordinary desired-state convergence and has its own deadline.
Use meta { subscribe = [file.config] } or notify on the source to request refresh. Dependency ordering alone is insufficient.
Systemd daemon reload barrier
With auto, managed .service files and .service.d/*.conf files beneath /etc/systemd/system, /usr/lib/systemd/system, and /lib/systemd/system precede service work. Changes coalesce into one journaled systemd daemon-reload barrier. Systemd-reported stale definitions can also request it.
This is a shared prerequisite action, separately reported from service restart/reload. never disables the automatic barrier. Automatic reload also requires refresh.journal.v1.
Refresh intent and outgoing causal obligations are durable. Interruption can lead to repeated delivery; recovery explains the limits.
Plugin providers
Approved subprocess plugins can add backends with provider = "NAME" and a matching unlabelled child block for typed options. A child block for another provider fails validation. The normalized options, provider schema version, and implementation identity are pinned into the catalog and recovery digests.
provider = "auto" chooses the applicable backend with the highest declared priority using the compilation target’s capabilities. A tie or no applicable backend fails. With only built-ins installed, systemd is selected when its capability is available. Use an explicit provider when selection must remain fixed as installations change.
Backends declare supported fields, refresh modes, capabilities, normalized identity, physical ownership, affected paths, and optional shared definition actions. Rivet retains property comparisons, verification, notification coalescing, startup refresh consumption, and reporting. Omitted properties remain unmanaged; unsupported requested properties are rejected. Plugin definition actions use the same durable shared-action mechanism as systemd’s barrier.
To write a backend, see the SDK guide.