ruvector/crates/rvforge-reader
rUv cbf9f6d7b6
feat: rvForge — one canonical RVF to signed platform installers (ADRs 283-293) (#790)
* chore: gitignore Hailo venvs, .ruvnet-brain scratch dirs, coverage output

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* docs: rvForge ADRs 283-293 + canonical requirements (ADR-283 master, RVM integration 284-293)

One canonical RVF to signed platform installers: @ruvector/forge CLI,
hosted build service, Tauri RVF Reader, rvm-* backend crates. Derived
from the rvForge product directive; requirements.md is the source of
truth for the feat/rvf-forge build-out.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* docs: RVForge platform spec (Store/Reader/Publisher/Registry/Enterprise) + naming

Adopt RVForge capitalization; publisher CLI is @ruvector/rvforge.
Adds marketplace objects, trust levels, review pipeline, security/
countersigning model, licensing, enterprise governance, and platform
acceptance test to the canonical requirements. Seeds loop-state.md for
the overnight build loop.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* docs: ADR-294 — RVForge platform (store, registry, trust system)

Five products (Store/Reader/Publisher/Registry/Enterprise), immutable
predecessor-linked releases, four trust levels, review pipeline,
countersigning + revocation semantics, licensing, enterprise override.
Documents the @ruvector/rvforge naming supersession.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 2 — forge-core crate agent spawned

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* docs: RVForge registry data model v0.1 (content-addressed, predecessor-linked)

Wire-format contract for publisher CLI, Reader, and registry: canonical
JSON identity rules, Release/PublisherRecord/CapabilityManifest/
WitnessReceipt/Revocation/TransparencyLogEntry objects, local storage
layout. Revocation blocks execution, never deletes local RVFs (ADR-294).

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* ci: RVForge 3-OS build matrix for CLI package and rvf-forge-core crate

Path-filtered workflow: npm install/build/test for the CLI on
ubuntu/windows/macos, cargo test + clippy -D warnings + fmt check for
the crate. Tolerates the pending forge->rvforge package rename and
skips gracefully while directories are still landing.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* docs: ADR-291 compatibility matrix v1; reader scaffold in flight

Machine-readable runtime-profile/packaging/output matrix the CLI vendors;
wasm and os-isolation+wasm supported, microvm and rvm-native planned with
explicit isolation claims per ADR-285.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* feat(rvforge): @ruvector/rvforge CLI — validate/build/verify with local RVF inspection

Publisher/build CLI per ADR-283 §4: init, validate (local, inspection-
only, never executes RVF content), build (local mode: canonical build
manifest + staged bundle + checksums + provenance), submit/status/
download (hosted API client, FORGE_API_URL), verify (checksum + prove-
nance recheck). Stable FORGE_E_* error codes, --json unattended mode,
73 jest tests green across 5 suites with synthetic RVF fixture.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state — CLI step 1 complete

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* ci: install rvforge CLI standalone (--workspaces=false)

Plain npm install inside npm/packages/rvforge resolves the parent npm
workspace and fails EBADPLATFORM on platform-pinned siblings
(router-darwin-arm64 on linux runners). Verified clean install + 73
tests green locally with the flag.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 6

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* feat(rvf-forge-core): inspection-only RVF packaging/verification crate

Per ADR-283/290/291: container inspection without execution, Ed25519
root-manifest + per-segment hash verification with unsigned-executable-
segment rejection, deterministic canonical build manifest (ADR-291
contract fields), provenance records, SHA256 checksum manifests, stable
wire error codes mirroring the CLI. 103 unit tests + integration
pipeline test, clippy -D warnings and fmt clean.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state — core crate step 2 complete

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 7 — packaging+witness agent spawned

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* docs: RVForge Agent Dock spec (D1-D8) — security/control surface

Collapsed pill + expanded trust view, 8 agent states, RVForge-owned
chrome vs agent content separation (spoofing defense), per-platform
placement, capability card, event-threshold noise control, 5s/2-action
termination acceptance test. ADR-295 in flight; dock implementation
queued behind reader scaffold in loop plan.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state — scope widened to full ADR-283..295 implementation

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* feat(rvforge-reader): Tauri v2 Reader scaffold + ADR-295 Agent Dock spec

Reader (standalone workspace, excluded from root): verify/capability-
card/runtime screens as framework-free static UI, runtime selection
implementing the FR004 ladder from the vendored compatibility matrix,
P6 capability contract rendering with vague-scope rejection, ADR-288
state-capsule layout (encryption stubbed, marked), inspect stubbed
pending rvf-forge-core FFI. 39 tests green, cargo check clean, parent
workspace unaffected. ADR-295: dock chrome RVForge-owned, agent content
strictly separated.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state — reader scaffold + ADR-295 landed

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 10 — dock-impl spawned

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 11

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* feat(rvforge): embedded/thin packaging, compat enforcement, inventory, witness chains

FR001/FR002: embedded mode with cross-target identical-RVF-hash
invariant (build fails on divergence), thin-mode signed locators with
round-trip verification. ADR-291 compat-matrix enforcement with
closest-supported suggestions. Deterministic software inventory (§3.9).
Hash-chained witness receipts (receipts.jsonl) on build/verify with
broken-chain detection. 137 jest tests green across 9 suites.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state — steps 6+7 CLI side complete

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 13 — publisher-verbs spawned

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 14

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* feat(rvforge-registry): content-addressed local registry with transparency log

ADR-294 MVP: canonical-JSON content addressing (id excludes signatures),
typed registry objects, ed25519 release-publish rules (bad-sig/revoked-
key/lineage violations typed), trust levels raisable only by registry
signature, non-destructive revocation (blocks execution, reads preserved
— tested), Merkle transparency log with inclusion proofs + tamper
detection, witness receipt chains on publish/revoke/verify. Reuses
rvf-forge-core canonical/error patterns. 67 tests, clippy+fmt clean.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state — registry crate landed (P2-impl, P4)

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* feat(rvforge-reader): ADR-295 Agent Dock — typed trust boundary, states, roster

Trust boundary enforced structurally: AgentProvidedStatus (sanitized
task text + progress only) composed separately from SystemOwnedStatus
(state, trust badge, network, permissions, witness, cost) — agent input
cannot reach system fields by construction. Sanitizer strips ANSI/
control chars, caps length, flags system-label mimicry as suspicious.
8-state machine (pause/terminate always one action; quarantine/
capability-denied not agent-exitable), attention-priority roster
(approval > denial > error > running), D8 event thresholds, pill +
expanded UI with visually distinct system chrome. 90 reader tests green.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state — Agent Dock implemented (P5)

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* docs: ADR status updates — 291/295 Implemented, 283/294 Accepted-in-progress

Living-plans sync: statuses now reflect what is actually on the branch,
with Updated notes naming landed scope and remaining gaps.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 17

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* ci: cover rvforge-registry and rvforge-reader in the RVForge matrix

Registry tests/clippy/fmt ride the existing core job; the reader gets
its own 3-OS job run inside its standalone workspace directory.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 18

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* docs: acceptance traceability matrix — merge gate for PR #790

Maps every §15/platform/dock criterion to automated evidence or a named
DEFERRED blocker (clean-OS installs, notarization, cross-repo rvm
runtime). Merge gates on green AUTOMATED rows across 3 OSes.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 19

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* feat(rvforge): publisher verbs pack/test/publish with local registry

pack: P4 validation (structure, capability specificity with ADR-294
manual-review-trigger flagging, compat, inventory, license), draft
Release + CapabilityManifest objects. test: inspection-only subset of
the 10 P4 categories with honest 'skipped: requires quarantined runtime'
for execution-dependent ones; tampered variants rejected. publish:
ed25519-signed content-addressed writes to the registry-model layout
(predecessor lineage, transparency log, witness receipt); keygen via
node:crypto; key files never logged, world-readable keys refused.
220 jest tests green across 13 suites.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state — publisher verbs landed (P1)

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 21 — parity-check spawned

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* feat(rvforge-reader): real rvf-forge-core verification + encrypted state capsules

Inspect/verify now call rvf-forge-core (inspection-only, verification
before any load, witness record per verification appended to the state
dir per ADR-284 req 9); capability card derives from real declared
capabilities and refuses to render unverified; state capsules encrypted
(ChaCha20-Poly1305, per-install key, 0600 perms) with base-RVF lineage
binding and mismatch rejection per ADR-288. 113 reader tests green.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state — reader FFI landed

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* docs: ADR 284-293 status sync against landed implementation

284/285/286/288/289 -> Accepted with precise landed-scope notes;
287/290/292/293 stay Proposed with honest gap notes (hosted service,
rvm runtime — cross-repo). Living-plans discipline: every status now
matches the code on this branch.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 23

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 24 — parity in progress, CI 7 green / 0 red

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 25 — witness-viewer spawned

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* feat(rvforge): CLI<->Rust registry parity — proven interoperable

rvforge-registry-check binary validates any registry dir (content
addresses, release rules, lineage, log inclusion, witness chains);
scripts/rvforge-parity-check.sh publishes two lineage-linked releases
through the real CLI and validates with the Rust crate — PARITY OK.
CLI canonical-JSON/id divergences fixed on the CLI side per contract.
CI parity job added (ubuntu). Registry 92 tests, CLI suites green.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state — parity landed, PARITY OK

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore(rvforge): prepublishOnly gate (build+test) before any npm publish

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 27

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 28 — acceptance snapshot green, CI 6/0/48

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* feat(rvforge-reader): witness viewer — hash-chain verification screen + dock wiring

P15.11: loads reader/CLI receipts.jsonl, verifies per-subject content-id
+ prevReceipt continuity, renders chronological chains with exact
broken-at-N indicators; dock witness-status element now reflects real
chain state. Entirely system-owned chrome (ADR-295). Tamper/reorder/
empty cases tested. 133 reader tests green.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state — witness viewer landed; all workstreams complete

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 30 — awaiting full-green CI (0 failures)

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 31 — CI 12/42/0

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 32 — CI 31/23/0, parity green in CI

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 33 — CI 29/25/0

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* fix(rvf-forge-core): classify rooted paths uniformly across platforms

Windows CI failure: '/etc/hostname' has a root but no drive prefix, so
is_absolute() is false on Windows and the path took the relative branch
with a different rejection message than the test (and Linux) expected.
Branch on has_root() instead — any rooted path goes through the
containment check on every platform. Refusal behavior unchanged; only
classification is now uniform. Linux gate re-verified: 117 tests,
clippy, fmt green.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 34 — windows path-classification fix pushed

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 35 — post-fix CI clean, re-running

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 36 — CI 32/22/0, fix verified

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 37 — CI 33/21/0

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state iteration 38 — CI 32/22/0

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx

* chore: loop-state — final verdict, proceeding to merge on documented basis

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01ParP55bZs2iTGEGvpnUecx
2026-08-04 08:13:26 -03:00
..
assets feat: rvForge — one canonical RVF to signed platform installers (ADRs 283-293) (#790) 2026-08-04 08:13:26 -03:00
capabilities feat: rvForge — one canonical RVF to signed platform installers (ADRs 283-293) (#790) 2026-08-04 08:13:26 -03:00
icons feat: rvForge — one canonical RVF to signed platform installers (ADRs 283-293) (#790) 2026-08-04 08:13:26 -03:00
src feat: rvForge — one canonical RVF to signed platform installers (ADRs 283-293) (#790) 2026-08-04 08:13:26 -03:00
tests feat: rvForge — one canonical RVF to signed platform installers (ADRs 283-293) (#790) 2026-08-04 08:13:26 -03:00
ui feat: rvForge — one canonical RVF to signed platform installers (ADRs 283-293) (#790) 2026-08-04 08:13:26 -03:00
.gitignore feat: rvForge — one canonical RVF to signed platform installers (ADRs 283-293) (#790) 2026-08-04 08:13:26 -03:00
build.rs feat: rvForge — one canonical RVF to signed platform installers (ADRs 283-293) (#790) 2026-08-04 08:13:26 -03:00
Cargo.lock feat: rvForge — one canonical RVF to signed platform installers (ADRs 283-293) (#790) 2026-08-04 08:13:26 -03:00
Cargo.toml feat: rvForge — one canonical RVF to signed platform installers (ADRs 283-293) (#790) 2026-08-04 08:13:26 -03:00
README.md feat: rvForge — one canonical RVF to signed platform installers (ADRs 283-293) (#790) 2026-08-04 08:13:26 -03:00
tauri.conf.json feat: rvForge — one canonical RVF to signed platform installers (ADRs 283-293) (#790) 2026-08-04 08:13:26 -03:00

RVForge Reader

Tauri v2 desktop reader for signed .rvf agent packages — the host described in ADR-289. RVF reading, verification, capability derivation, runtime selection, and encrypted state capsules are real; installing and running an agent are not, because they need rvm-ffi, which does not exist yet.

Layout

src/inspect.rs      read and verify a package without executing it
src/receipts.rs     the local witness log — one record per verification result
src/capability/     derive the P6 install-time capability contract
                    mod.rs the rules, text.rs the sentences users consent to
src/runtime.rs      apply the FR004 runtime selection order
src/state/mod.rs    ADR-288 state-capsule layout and lineage rules
src/state/crypto.rs XChaCha20-Poly1305 sealing, per-install key
src/dock.rs         ADR-295 dock entry: the agent/system split, in the types
src/dock_text.rs    sanitizing and screening agent-authored strings
src/dock_state.rs   the eight dock states and who may move between them
src/dock_roster.rs  multi-agent policy and the two-interaction control path
src/dock_events.rs  D8 event thresholds — what may interrupt
src/commands.rs     Tauri command wrappers (feature `desktop`)
src/lib.rs          module root + the security invariants this crate holds
ui/                 four screens, plain HTML/CSS/JS, no build step
assets/             vendored copy of compatibility-matrix.json
capabilities/       Tauri v2 ACL: file dialog only
tests/              runtime selection, capability card, inspection, state, dock

Build and test

The crate is a standalone workspace (its Cargo.toml carries an empty [workspace] table) and is listed in the repo root's workspace.exclude. A Tauri app pulls a large, app-specific dependency graph — webview bindings, bundler, plugin build scripts — that every cargo build --workspace in the parent repo would otherwise pay for.

Tauri is behind an optional desktop feature, so the core logic builds and tests without a webview. rvf-forge-core is a path dependency on a member of the root workspace; that works across the workspace boundary because it inherits its own workspace's version and lints.

cd crates/rvforge-reader
cargo check          # core only — no webview packages needed
cargo test           # 113 tests, no Tauri dependency

This is the CI-testable path. Building the desktop shell additionally needs the platform webview development packages (libwebkit2gtk-4.1-dev, libjavascriptcoregtk-4.1-dev, libsoup-3.0-dev on Debian/Ubuntu; WebView2 on Windows; Xcode command line tools on macOS):

cargo check --features desktop
cargo run   --features desktop     # launches the window

cargo tauri dev and cargo tauri build need the CLI (cargo install tauri-cli --version '^2'). There is no Node toolchain: the frontend is static files under ui/, referenced by build.frontendDist.

Before producing installers, regenerate the icon set with cargo tauri icon icons/icon.png — the committed PNGs are placeholders and there is no .ico or .icns yet, which Windows and macOS bundling require.

Inspection, verification, and receipts

inspect::inspect calls rvf_forge_core::inspect_bytes and reports the real identity (SHA-256 of the container), the segment inventory, and the capability classes the container declares. It checks nothing, so its verification status is always unverified.

inspect::verify calls rvf_forge_core::verify_bytes — root manifest present, per-segment content hashes, unsigned-executable refusal — and appends one receipt to <state_root>/receipts.jsonl, on a pass and on a refusal alike (ADR-284 §1 requirement 9). Both operations run against a single read of the file, so a package cannot be swapped between inspection and verification.

Neither executes package content.

What is not implemented

Gap Current behavior Needs
Publisher identity publisher: null, and a present signature reports not-checked rather than verified. A trust store mapping Ed25519 keys to publisher identities; VerifyOptions::trusted_keys is empty without one
Scoped capability grants The container declares classes with no scopes, so each granted class says so and raises a manual-review trigger. An unsigned <file>.rvf.manifest.json sidecar may narrow a class to a specific scope, never widen one. A signed CapabilityManifest segment inside the RVF
Install / Customize permissions Disabled buttons. The install flow
Emergency controls Disabled buttons on the Runtime screen. rvm-ffi lifecycle calls (pause, terminate, revoke, rollback)
Dock roster In-process, seeded by dock_add_scaffold_agent, which reports the unknown value for every security-bearing field (unverified publisher, no confinement, no witness chain). RVM telemetry over rvm-ffi; pause is rvm_suspend, terminate is rvm_terminate (ADR-295 implementation note)
Dock instruction field Disabled placeholder. The agent instruction channel, once rvm-ffi is wired in
Dock witness status "no witness chain". rvm witness export (ADR-289 §3)

Each of these reports absence, never a benign default. SignatureStatus distinguishes not-checked from verified for exactly this reason: a signature nobody could check is not a signature the user can rely on.

State capsules

state::seal / unseal use XChaCha20-Poly1305 — pure Rust, no unsafe, and constant-time on hosts without AES hardware, which the Windows, macOS, and Linux ARM builds cannot assume. The 192-bit nonce makes a random nonce per capsule safe without a counter, and a counter is what a state directory the user may copy, restore, or roll back would silently break.

One 32-byte install key lives at <state_root>/install.key, created 0600 on first use from the OS CSPRNG; per-capsule keys are HKDF-SHA256 derived from it with the base RVF identity as info. The base identity is written into the capsule header in the clear — so a foreign lineage is refused without needing a key — and is covered by the AEAD's additional data, so relabelling a capsule to another lineage fails authentication rather than opening as that lineage's state. Customer-held keys (ADR-288 §4) are constructible today through InstallKey::from_bytes; a key-management UI is not.

Security invariants

These are enforced in the library and covered by tests. They must survive whatever fills the gaps above.

  1. RVF content is never executed. No code path loads, links, or interprets a segment; the only bytes read from an executable segment are read to hash them. inspect and verify must be safe on an untrusted package (ADR-289 §3, ADR-284 §1 requirement 7).
  2. Verification precedes any load, and every result is witnessed. An unchecked package reports unverified rather than being assumed good, and a refusal is written to the receipt log exactly as a pass is.
  3. Capability rendering is default-deny, and no card without verification. Every one of the fifteen ADR-286 classes the container does not declare appears in the "cannot" list; a package that did not verify yields a card that grants nothing, as does a missing or rejected manifest.
  4. No vague permission prose. Broad scopes (all-files, *, unrestricted) and banned phrases such as "access your computer" are rejected at derivation time, not filtered in the UI (requirements P6).
  5. No network calls. Nothing here opens a socket. The packaged app's CSP has no remote origin in any directive, and the asset protocol is disabled.
  6. The runtime order is not configurable. It is read from the vendored compatibility matrix, which is compiled in via include_str! so that swapping a file on the installed machine cannot reorder it. ADR-289 permits reordering only by signed policy; that path is not implemented, and policy_source always reports embedded-default.
  7. Hosted mode does not claim bare-metal isolation. The card shows the isolation class the matrix records for the selected profile — os-sandbox+wasm, never partition (ADR-285).
  8. Agent input cannot reach dock chrome. An agent supplies task text and progress, through AgentProvidedStatus::from_agent(&str, i64) and nowhere else. State, trust badge, network indicator, permission summary, witness status, resource usage, and cost live in SystemOwnedStatus, whose fields are private and whose single constructor takes no agent-derived value (ADR-295 §7).

Today HostProfile::detect() claims no OS confinement, no KVM, and no measured boot, because the rvm-host adapters do not exist yet. Selection therefore lands on plain wasm. That is the honest answer, and it will move up the order as adapters land — not before.

Screens

  1. Open — pick or type a path, see file identity and signature status. Unverified renders as a warning, not a neutral state.
  2. Capabilities — the P6 contract: "This agent requests" beside "This agent cannot", with Install / Customize Permissions / Cancel.
  3. Runtime — selected profile, isolation class, mechanisms engaged, the selection order and where it came from, a per-profile eligibility table, and the emergency controls (Pause · Terminate · Disconnect Network · Revoke Capabilities · Rollback State) as disabled placeholders.
  4. Dock — the ADR-295 agent dock. A collapsed pill (icon · name · agent-reported task · progress · state · Pause · Stop · Expand), an expanded panel with the ten §2 elements and the seven-line §8 capability card, and the §5 multi-agent collapse: one active agent, two secondary icons, an overflow count, and aggregates computed over every agent including the hidden ones.

The dock trust boundary (ADR-295 §7)

Agent-authored text is confined to .agent-region — an inset, dashed, monospace block on a tinted background, preceded by a dock-drawn "Agent-reported" label and clipped to one line. It reaches the DOM only through textContent, so it cannot produce markup, a class name, or a state indicator. System chrome uses presentation the agent has no way to emit from inside that region.

Before it is rendered, agent text is stripped of ANSI escapes, control characters, bidi overrides and line breaks, capped at 120 characters, and then screened for chrome mimicry: a task string reading "Paused", "Stopped", "✓ Network disabled", or "Verified publisher" is withheld and flagged rather than drawn next to the dock's own labels. System-authored lines are cleaned but not screened, because "Running → Paused" written by the runtime is the label.

The state machine carries the same rule: an agent may assert Idle, Running, WaitingForApproval, Error, or Completed, and nothing else. It cannot put itself into Paused, CapabilityDenied, or Quarantined, and it cannot leave them. Pause and terminate are legal for the user from every non-terminal state, in one call, with no confirmation chain — which is what keeps the acceptance test ("identify, read, inspect permissions, terminate, in two interactions") satisfiable: expand carries the permissions and the capability card, terminate is reachable from the collapsed pill.

Event thresholds (§9) allow only approvals, policy violations, cost limits, failures, and milestones to interrupt. Progress, tool calls, network samples, and state changes accrue silently into the expanded panel's recent actions.

Keeping the matrix in sync

assets/compatibility-matrix.json is a vendored copy of docs/research/rvf-forge/compatibility-matrix.json. A test compares the two byte-for-byte and fails on drift; update both together.