ADR-0005 — Cross-distro packages
- Status: Accepted (2026-07-24, Opus review)
- v0.9 note (2026-07-25): the predicted absorption happened.
brewis now a column in the curated map and Repology’shomebrewrepo feeds the generated one (ADR-0007). Homebrew casks are deliberately excluded: a macOS application bundle has no Linux counterpart, so mapping one would be exactly the silent guessing this ADR rejects. - Related: ROADMAP v0.3, CODE_REVIEW M5, ADR-0001
- Acceptance note: Confirmed. v0.2 keeps
PackageManifestbyte-stable on disk (no unification, no new fields) to preserve identical behaviour; thelogicalfield and the mapping table are introduced in v0.3, added as serde#[serde(default)]so older snapshots stay readable. Within-family restore is the guaranteed floor; cross-family is best-effort-with-warnings, never silent guessing.
Context
Section titled “Context”Once Rivet discovers packages via multiple PackageManagers (ADR-0001), a hard
truth surfaces: the same software has different package names on different
distros. fd is fd-find on Debian; ripgrep’s binary is rg; Python is
python/python3/python311 depending on the distro. A naive “install the Arch
names on Fedora” restore will partially and silently fail. This is the central
difficulty of OS-agnostic environment restore, so it needs an explicit policy
rather than an accidental one.
Decision
Section titled “Decision”Model a logical package distinct from a manager-specific name, and adopt a conservative default: perfect fidelity within a distro family, honest warnings across families — never silent guessing.
pub struct Package { pub manager: String, // "pacman", "apt", ... (as captured) pub name: String, // manager-native name (as captured) pub logical: Option<String>,// resolved cross-distro identity, if known}Resolution layers, in order:
- Same family (default, always exact). Arch→Arch, Debian→Ubuntu, etc. restore uses the captured native names verbatim. No mapping risk.
- Curated mapping table (shipped data, community-extendable) keyed by
logical id → per-manager name, e.g.
ripgrep = { pacman="ripgrep", apt="ripgrep", dnf="ripgrep", brew="ripgrep" },fd = { pacman="fd", apt="fd-find", dnf="fd-find", brew="fd" }. Only high-confidence entries are included. - Unresolved → warn, never guess. A package with no mapping for the target
manager becomes a restore-plan warning (“
foo: no known Fedora equivalent — install manually or add a mapping”), listed but not attempted.
The mapping table is data, not code (a versioned TOML/JSON asset), so
contributors extend coverage without touching logic, and users can add local
overrides in rivet.toml. MachineMetadata records distro family+version so
restore knows when it is crossing a boundary and should surface warnings.
Cross-family restore is therefore opt-in-honest: it does as much as it can confidently, and clearly reports the rest instead of producing a broken machine that looks complete.
Alternatives considered
Section titled “Alternatives considered”- Guess by fuzzy name matching. High risk of installing the wrong package; violates “understand, don’t blind-copy.” Rejected.
- Same-family only, no mapping ever. Simple and safe, but abandons the OS-agnostic promise for the (common) case of migrating Arch→Fedora. Rejected as the ceiling; adopted only as the guaranteed floor.
- Depend on an external universal-package DB (repology, etc.) at runtime. Great data, but a network dependency and licensing/availability coupling at the worst moment (a fresh machine mid-migration). Instead, optionally generate our shipped table from such sources offline at build time.
- Push everything to Flatpak/Nix for portability. Elegant but changes the user’s chosen package model; offered as an option, not imposed.
Consequences
Section titled “Consequences”- Positive: honest, safe cross-distro restore; contributors grow coverage via data PRs; same-family migrations are perfect.
- Positive: the same
logicalconcept later absorbs Homebrew/winget for macOS/Windows. - Cost: an ongoing mapping table to curate; partial cross-family restores by design (with clear warnings) rather than a false promise of completeness.
- Risk: users may expect 100% cross-family fidelity — set expectations in docs: Rivet guarantees within-family, best-effort-with-warnings across.