ADR-0004 — Plugin contract
- Status: Accepted (2026-07-24, Opus review)
- Related: ROADMAP v0.2/v0.6, CODE_REVIEW M4
- Acceptance note: Confirmed. v0.2 delivers only the internal
Plugintrait and migrates today’s static groups onto it (fixing modes/symlinks incollect). Declarative TOML plugins andrivet trackare v0.6. The trust model (explicit, reviewed, dry-run-previewed hook commands) is non-negotiable.
Context
Section titled “Context”Two Charter goals converge here: “every supported application is a plugin, added
without changing Core” and the user’s request to “track any file” and treat the
tool like “Terraform/Ansible for your environment.” Today plugins.rs is a
compile-time PLUGINS: &[PluginDefinition] array of static paths. Consequences:
- Users cannot add an app or a custom directory without editing Rust.
- Plugins cannot carry per-app logic (VS Code extension export/import, GNOME
dconfdump/load,crontab -l), so those environments restore incompletely. - Adding a group does currently require a Core recompile — contrary to the Charter’s “no Core changes” principle.
Decision
Section titled “Decision”Split into an internal Plugin trait (built-ins, v0.2) and an external
declarative plugin format plus user file tracking (v0.6).
Internal contract (v0.2, rivet-core)
Section titled “Internal contract (v0.2, rivet-core)”pub trait Plugin { fn id(&self) -> &'static str; fn discover(&self, ctx: &HostContext) -> Vec<Candidate>; // present, named groups fn collect(&self, sel: &Selection) -> Result<Vec<TrackedFile>>; // real mode + FileKind fn plan_restore(&self, snap: &SnapshotTree) -> Vec<RestoreAction>; fn validate(&self, snap: &SnapshotTree) -> Vec<Warning>; fn hooks(&self) -> Hooks { Hooks::default() } // optional export/import commands}collect is where CODE_REVIEW H1/H2 are fixed: it records the real file mode and
emits FileKind::{Regular,Directory,Symlink} (in-root links preserved,
out-of-root flagged). hooks lets a plugin declare commands like
code --list-extensions (export) / code --install-extension (import) and
dconf dump / dconf load.
Declarative plugins (v0.6)
Section titled “Declarative plugins (v0.6)”Built-ins ship as data and users drop in their own — no compilation:
id = "helix"title = "Helix editor"paths = [".config/helix"]packages = { pacman = ["helix"], apt = ["helix"], brew = ["helix"] }
[hooks]export = [] # optional shell commands, explicit + reviewedimport = []A loader in rivet-plugins reads these into the same Plugin interface the
built-ins implement, so there is one code path.
Arbitrary file tracking (v0.6)
Section titled “Arbitrary file tracking (v0.6)”# in the snapshot's rivet.toml[[track]]path = "~/dev/scripts"include = ["**/*.sh"]exclude = ["**/node_modules/**"]secrets = "vault" # plaintext-blocked | vault (see ADR-0003)mode = "preserve"Plus imperative helpers rivet track <path> / rivet untrack <path> that edit
that block. Tracked entries flow through the same collect/plan_restore path
as plugins, inheriting all safety checks (exclusions, secret scan, safe_relative,
rollback).
Trust model
Section titled “Trust model”Hooks and tracked commands are explicit, user-authored, reviewable strings —
never fetched or inferred. Declarative plugins run no code except the commands
their author wrote. Rivet shows hook commands during scan/dry-run before they
ever execute. A future signed-plugin registry is post-1.0 (ROADMAP ecosystem).
Alternatives considered
Section titled “Alternatives considered”- Keep the static array, add groups in Rust. Contradicts the Charter and the user’s “track any file” ask. Rejected.
- WASM/dynamic-library plugins now. Powerful but heavy; a stable ABI is premature pre-1.0. Declarative TOML covers ~all real dotfile/app cases first.
- Arbitrary tracking without the secret/exclusion pipeline. Would reintroduce the blind-copy risk the Charter forbids. Rejected — tracking reuses the guards.
Consequences
Section titled “Consequences”- Positive: users extend Rivet without Rust; per-app export/import makes restores actually complete; one interface for built-ins, user plugins, and tracked paths.
- Positive: delivers the “declarative, reproducible environment” feel.
- Cost: a manifest schema to design and version; hook execution is new surface area — mitigated by the explicit/reviewed trust model and dry-run preview.
- Risk: schema churn pre-1.0; acceptable while versioned, frozen at v0.9.