How it works
A Rivet run has two halves. Compilation turns configuration and host facts into a bundle and never touches the host. Application runs on the host, compares each resource with its current state, and changes what differs.
The machine that compiles is the controller. It can be your laptop, a CI job, the host itself, or the compile service. The program that applies a bundle on the host is the runner. The same runner is used whether you apply locally, over SSH, or through an agent.
Facts
rivet facts collects a JSON snapshot of one host:
- Platform: distribution, version, architecture, and init system.
- Identity: a hash of the machine ID.
- Capabilities: which resource implementations the host can support, such as
service.systemd.v1when systemd is running orpackage.apt.v1when apt is installed. See capabilities. - Inventory: CPUs, memory, disks, mounts, network interfaces, cloud metadata, and any custom facts you add.
Configuration reads these as facts.os.name, facts.processors.count, and so on. Compiling against a host’s real facts binds the bundle to that host. Apply refuses a bundle compiled for a different machine, or one that needs a capability the host lacks.
Compilation
rivet compile loads every *.hcl file in a directory and evaluates it: variables, modules, for_each and count expansion, functions, templates, and data hierarchies. The result is a catalog, a list of resources with fully evaluated values and the relationships between them.
Relationships come from three places:
- Explicit metadata:
depends_on,before,notify, andsubscribein a resource’smetablock. - References: using
file.config.pathin another resource makesfile.configits prerequisite. - Resource rules: a file’s declared parent directory is ordered first, and a user is ordered after the groups it belongs to.
Compilation then collects every asset (inline content, local files, objects from S3 or GCS, .deb archives), verifies checksums, and writes the catalog and assets into a bundle. The bundle is an uncompressed tar archive written with mode 0600. rivet graph shows the order resources will run in and why.
Bundles can contain secrets in plain text. Marking values sensitive keeps them out of reports and diagnostics; it does not encrypt the bundle. See catalogs and bundles.
Plan and apply
rivet plan inspects each resource on the host and reports what would change, without writing anything. It cannot always predict everything. If a package install would change, for example, a dependent file is reported as deferred because its outcome depends on the package.
rivet apply visits resources one at a time in dependency order. For each one it:
- Inspects the current state.
- Records its intent in the journal.
- Makes the change.
- Inspects again to verify the result.
- Marks the work complete and queues any notifications.
If a resource fails, resources that depend on it are blocked, and unrelated resources continue. There is no host-wide transaction and no automatic rollback. The report says which resources changed, which failed, and which may have partially changed. See plan, apply, and reports.
Notifications and refreshes
When a resource with notify makes a verified change, Rivet queues a refresh for each target: a service restart or reload, or a command marked refresh_only. Several changes notifying the same service result in one restart, after all of them have finished.
Pending refreshes are stored in the journal. If a run changes a config file and then crashes before restarting the service, the next run performs the restart. As a result, a refresh can occasionally run twice; Rivet prefers that to skipping one.
The journal and recovery
The journal is a SQLite database in /var/lib/rivet. It records runs, in-flight changes, and pending refreshes. It is not an inventory: Rivet always re-inspects the host and never trusts the journal’s view of current state.
After an interruption, the next apply removes Rivet’s own temporary files, re-inspects the affected resources, and converges them again. Commands re-check their guards, so one without a guard (always = true) runs again. rivet journal shows unresolved work without changing anything. Don’t delete the journal to force a clean run. See recovery and the journal.