Compatibility policy
What a Rivet release promises about data written by a different Rivet release, and which interfaces you can build against without expecting them to move.
This policy is in effect — it took hold at v1.0.0, and the current release is v1.0.1. It does not extend backwards: the v0.x series made no such promise and changed the snapshot format once along the way (v1 → v2, in v0.3).
The short version
Section titled “The short version”| You have | You can | Under v1.x |
|---|---|---|
| A snapshot written by v1.x | Read and restore it with any later v1.y | Guaranteed |
| A snapshot written by v1.x | Read it with an earlier v1.w | Only if it uses no newer optional fields |
A rivet.toml from v1.x |
Use it with any later v1.y | Guaranteed |
| A declarative plugin from v1.x | Load it with any later v1.y | Guaranteed |
--json output |
Parse it in a script | Guaranteed; fields may be added |
| The Rust library crates | Call them from your own code | No guarantee — see below |
The single sentence version: a v1.x Rivet restores any snapshot any v1 Rivet ever wrote. Everything else on this page is detail about how that is achieved and where it stops.
The frozen formats
Section titled “The frozen formats”Four on-disk interfaces are frozen at v1.0.
1. The snapshot manifest — manifest.toml
Section titled “1. The snapshot manifest — manifest.toml”Carries format_version = 2. Version 2 is the v1.0 format, and it is the
version a v1.x Rivet writes.
Version 1 manifests (pre-v0.3, Arch-only) are still migrated in memory on
read, and the next snapshot rewrites the repository as version 2. That
migration path stays for the whole v1.x line.
2. The repository layout
Section titled “2. The repository layout”A snapshot repository contains rivet.toml, manifest.toml, files/
mirroring paths relative to the source user’s home directory, secrets/ for
age ciphertext, and a generated README.md. Those names and that meaning
are frozen. New sibling entries may be added; existing ones will not be
renamed or repurposed.
3. rivet.toml — the selection policy
Section titled “3. rivet.toml — the selection policy”The approval file: selected groups, package sources, [[backend]],
[vault], [[secret]], [[track]], [profile.<name>].
It carries no version field, deliberately. Every field is #[serde(default)]
and every default is the conservative direction — an unrecognised or absent
table means not selected, never selected. Rivet therefore never broadens
what it captures because it read an old config, which is the failure mode a
version field would exist to prevent. New tables may be added; existing keys
keep their names and meanings through v1.x.
4. The declarative plugin schema
Section titled “4. The declarative plugin schema”A plugin TOML file declares id, title, description, roots, packages,
and hooks. Unknown keys are warned about and ignored rather than rejected,
so a plugin written for a later v1.y still loads on an earlier one — it simply
does less.
What “additive” means
Section titled “What “additive” means”Through the v1.x line, a change to a frozen format may only:
- add a new optional field with a
serdedefault, - add a new value to an existing list, or
- add a new variant to an enumerated field (
os_family,distro_family).
It may not rename a field, change a field’s type, change what an existing
value means, or make a previously optional field required. Any of those is a
2.0 change, and would come with both a format_version bump and a migration.
The practical consequence of “additive only” is the one asymmetry in the table at the top: forward compatibility is degraded, not broken. v1.0 reading a snapshot written by v1.5 parses successfully and ignores anything v1.5 added. That is usually right, but it means the older Rivet may restore less than the snapshot contains. When it matters, restore with a Rivet at least as new as the one that captured.
Unknown platforms read as unknown
Section titled “Unknown platforms read as unknown”The third bullet is worth stating explicitly, because it is the case that would otherwise turn “degraded” into “broken”.
machine.os_family and machine.distro_family are enumerated. A snapshot
from a future Rivet that supports a platform this one has never heard of —
os_family = "windows", say — reads that field as unknown and carries on.
It does not fail to load.
That behaviour is a promise, not an implementation detail. Rivet’s own
capture never writes unknown for a host it successfully identified, so the
value means exactly one thing on read: this snapshot came from somewhere this
binary does not know about. A restore treats it as a cross-platform restore
and warns accordingly.
The guarantee was added in v0.9 (ADR-0007), before the freeze, for the obvious reason: after v1.0 there is no way to give it to binaries already installed.
--json output
Section titled “--json output”Treated as a supported interface, documented in JSON.md, and governed by the same additive rule: fields may be added, never renamed or removed, within v1.x. A consumer must ignore fields it does not recognise.
Two properties scripts may rely on: errors are never JSON (stdout stays empty,
the diagnostic goes to stderr, exit status is non-zero), and absent optional
values are null rather than omitted.
Minimum supported Rust version
Section titled “Minimum supported Rust version”Rivet’s MSRV is 1.88. It is declared in [workspace.package] and enforced
by CI’s msrv job, which reads the version out of Cargo.toml and builds every
target with exactly that toolchain — so the number on this page is one that has
compiled, not one that was hoped for.
This matters to you if you install with cargo install rivetsnap-cli, build
from source, or depend on the library crates. It does not matter if you use a
prebuilt binary or the Homebrew tap.
| Change | Version bump | Example |
|---|---|---|
| MSRV stays the same | Patch or minor | Most releases |
| MSRV rises | Minor, never patch | A dependency raises its own floor |
| MSRV falls | Any | Never a breaking change |
Raising the MSRV in a patch release would break a build that worked yesterday on a toolchain the user did not change, so it is treated as a feature-level change and gets a CHANGELOG entry naming the new floor.
An MSRV that only holds for src/ is not one a contributor can work against,
so the CI job builds --all-targets: tests and benches must compile on it too.
What is not frozen
Section titled “What is not frozen”Be explicit about this, because “1.0” is often read as “all of it is stable”.
-
The Rust library crates.
rivetsnap-core,-platform,-backend,-plugins,-crypto, and-secretstoresare published so that plugin authors and downstream tools can build against the same types, but their Rust APIs are not covered by this policy in the v1.x line. Only therivetbinary’s behaviour and the four on-disk formats are. If you depend on the libraries, pin an exact version. -
CLI output intended for humans. Prose, colour, table layout, and progress rendering may change in any release. Parse
--json, never stdout. -
~/.local/state/rivet/. Restore selections, rollback copies, and caches are local working state, not an interface. Its layout may change. -
SECRET_SCAN_VERSION(currently1). This is a safety counter, not a format: it rises whenever the credential scanner’s rules change, and a manifest carrying an older value simply forces a full re-scan. Rising is always safe and is expected within v1.x. -
The package map. Curated and Repology-derived mappings change as distributions do. That is the point of it.
Deprecation
Section titled “Deprecation”If something frozen here has to go in 2.0, it gets, in order: a CHANGELOG entry naming it, at least one v1.x release that warns at runtime when the deprecated form is used, and a documented migration path. Nothing frozen disappears without all three.
Reporting a compatibility break
Section titled “Reporting a compatibility break”A v1.x Rivet failing to read or restore a snapshot written by another v1.x Rivet is a bug, not a limitation — report it. If the failure involves the vault or a sealed secret, use private vulnerability reporting rather than a public issue.
Related
Section titled “Related”- JSON.md — the
--jsonschemas this policy covers - ADR-0005 — the v1 → v2 manifest change
- ADR-0003 — the vault and
secrets/layout - VERIFYING_RELEASES.md — verifying the binary itself