Facts and custom collectors
rivet facts prints a JSON snapshot of a host with three parts:
target: distribution, version, architecture, init system, hashed machine identity, and capabilities. Compilation and apply use these to check the bundle fits the host.collected_at_ms: when the snapshot was taken.facts: inventory, described below, which configuration reads asfacts.<group>....
Collection only reads. Groups whose sources aren’t available are left out, as are individual fields that can’t be read. A failure to read the OS or machine identity is an error. Older snapshots without a facts section, and bare target files like examples/file/target.json, still work.
Built-in facts
These groups follow Puppet’s structured fact organization, using virtualization and uptime as group names. NUMA uses the kernel’s node sysfs interface; it describes host topology rather than process affinity or cgroup limits. Security reads runtime kernel controls and the EFI SecureBoot variable (after the efivarfs attribute prefix). Virtualization uses systemd-detect-virt --vm / --container, with hypervisor and container marker files as fallbacks. Both detection commands must report no virtualization before is_virtual is false.
Cloud facts come from the instance metadata service at 169.254.169.254: AWS IMDSv2, Azure IMDS, or GCP metadata. Requests skip proxies, time out after one second, and the whole cloud lookup is limited to five seconds. Only inventory fields are collected, never credentials or user data.
The public /run/cloud-init/instance-data.json cache is a fallback for unavailable API fields or providers without a native collector, such as OpenStack. API values take precedence, and cached fields are merged only for the same provider. AWS/GCP/Azure also have DMI detection when metadata is unavailable; attributes can then be empty. The sensitive cloud-init cache is never read. See cloud-init’s standardized instance data.
Networking uses sysfs for links and the installed ip command for address bindings; without iproute2, link facts still appear and address fields are omitted. Disk/filesystem identifiers come from sysfs, the optional udev database, and mounted filesystem records.
NVIDIA telemetry is read through the installed NVIDIA driver library; nvidia-smi isn’t needed. Without the driver library, GPUs still appear with their PCI and driver details, and readings that are unsupported or not permitted are left out. AMD and Intel GPUs currently show PCI and driver details only.
NVIDIA metrics follows the names in AWS’s GPU metric reference:
GPU telemetry describes the collection instant, like available memory and mount space; it is not an apply-time target constraint.
Use facts in HCL
Compile against a snapshot:
Nested paths, literal string keys, and literal array indices work:
facts.distribution, facts.version, facts.architecture, and facts.init_system are also available, including when compiling against a bare target file. Fact values keep their JSON types: whole numbers become integers, and other numbers stay fractional. Paths can use computed indices, splats, and for expressions, and integer facts can drive count. A missing fact or invalid index is a compile error.
Only facts that configuration actually uses affect the catalog digest. Values such as free memory, mount usage, and network addresses describe the moment of collection and aren’t rechecked during apply, so prefer stable facts when making decisions.
Custom JSON facts
Add your own facts by putting files in /etc/rivet/facts.d, or another directory passed with --facts-dir DIR:
The final extension is removed: potato.rb, potato.json, or potato supplies facts.potato. An executable file is executed directly (use a valid shebang); its stdout must contain one complete JSON value. A non-executable file is read as JSON regardless of extension. Objects, arrays, scalars, booleans, and null retain their types. The example exposes facts.potato.variety and facts.potato.count.
Files are processed in sorted filename order, non-recursively. Hidden files and directories are skipped; symlinks to regular files are followed. A missing directory supplies no custom facts. Duplicate basenames and collisions with any built-in fact or the four legacy aliases fail before custom executables run. Non-UTF-8 basenames, invalid JSON, unreadable files, failed execution, and nonzero exits fail collection without emitting a partial snapshot or exposing JSON/stderr in the error.
Executables run as the invoking user, with the facts directory as their working directory, stdin closed, and stderr discarded. Each executable has a ten-second budget; timeout/output-limit failures terminate its process group. JSON from each file and combined custom input is bounded to 1 MiB; the complete serialized snapshot must also fit the compiler’s 1 MiB input limit. Scripts must keep stdout free of diagnostic messages.
Custom executables run during rivet facts and each converging run, including noop/offline. Compile/validate and run --prepare-only --facts consume the saved snapshot. SSH compatibility probes and direct plan and apply collect only target identity and capability data, without full inventory collection or custom executables.
Inventory and verified identity
Facts are reported by the host itself, so they can’t prove who a host is. Cloud metadata and custom facts can be forged by anyone with root on the host. To classify hosts by verified identity, use the compile service and read trusted claims instead.