Skip to navigation

Resources and relationships

Rivet builds a directed graph of prerequisites and dependents. Execution follows a deterministic topological order, inspecting each resource immediately before its work.

Explicit ordering

file "directory" {
path = "/var/lib/app"
ensure = "directory"
}
file "config" {
path = "/var/lib/app/config"
content = "enabled\n"
meta { depends_on = [file.directory] }
}

depends_on lists prerequisites. before lists resources that must follow this resource. Both accept lists of handles, never string addresses. Whole-module references and expanded module families are also accepted for ordering. before creates a forward edge without a reverse expression edge.

Declare explicit absent states for removal. Declared file descendants automatically precede absent ancestors; mount removal precedes an absent mountpoint directory; key removal precedes removal of its managed user. Other absent resources do not automatically reverse dependencies, so account and group removal still needs explicit ordering.

Implicit ordering

References to handles or exported properties carry dependency provenance through locals and interpolation. Present users depend on managed primary and supplementary groups even when the relationship uses a literal group name or explicit GID. Group memberships depend on managed groups and listed users.

Services with automatic daemon reload depend on declared service-definition producers. Declared file ancestors, managed file owner and group identities, timer accounts, authorized-key users, mountpoint directories, and files beneath managed mounts also derive ordering. These resource-specific rules do not imply a dependency for every equal string.

Whole-module dependencies wait for completion of all descendants; exported resource handles retain their narrower resource dependency. See module boundaries and notifications.

Notifications

file "config" {
path = "/etc/app-config"
content = "enabled\n"
meta { notify = [service.app] }
}
service "app" {
name = "example-app"
ensure = "running"
on_change = "restart"
}

notify orders the declaring source before each refresh target. The inverse spelling is subscribe on the target: meta { subscribe = [file.config] }. Targets must be services or exec resources with refresh_only = true.

A dependency alone does not trigger a refresh. A notification becomes deliverable after a verified source change. Multiple sources coalesce into one target delivery after its prerequisites succeed. Modules can notify targets, and refresh-capable resources can subscribe to whole modules; delivery waits for successful module completion, including descendant refresh work. Accepted unchanged exec exits do not create new notifications.

Notifications are durable causal work. Recovery can deliver a refresh again after interrupted acknowledgement; exactly-once execution is not promised. Recovery explains how obsolete obligations are handled.

Failure and noop behavior

A failed prerequisite blocks dependent resources while unrelated resources continue. A noop prerequisite that would change defers dependent predictions/work. A converged noop prerequisite permits dependent work.

Missing references, invalid refresh targets, physical ownership conflicts, and graph cycles fail before the bundle is published. Inspect explained edges with rivet graph host.bundle --format json.