Files
wifi-densepose/vendor/ruvector/docs/adr/ADR-038-npx-ruvector-rvlite-witness-integration.md

8.9 KiB

ADR-038: npx ruvector & rvlite Witness Verification Integration

Field Value
Status Proposed
Date 2026-02-16
Deciders RuVector core team
Supersedes --
Related ADR-029 (RVF canonical format), ADR-032 (RVF WASM integration), ADR-037 (Publishable RVF acceptance test)

Context

ADR-037 introduced the publishable RVF acceptance test, which produces two artifacts:

  1. JSON manifest -- human-readable scorecards, ablation assertions, and SHAKE-256 witness chain
  2. .rvf binary -- native WITNESS_SEG (0x0A) + META_SEG (0x07), verifiable by rvf_crypto::verify_witness_chain()

ADR-032 added rvf_witness_verify and rvf_witness_count exports to rvf-wasm, enabling browser-side verification.

However, neither the npx ruvector CLI nor the rvlite browser runtime currently exposes witness chain verification to end users. The Rust rvf-cli has rvf verify-witness (17 subcommands), but the Node.js wrapper in npm/packages/ruvector/bin/cli.js does not surface it. Similarly, rvlite lists @ruvector/rvf-wasm as an optional peer dependency but does not call the witness verification exports.

This means an external developer who receives a .rvf acceptance test artifact currently needs the Rust toolchain to verify it. The goal is zero-friction verification via npx or a browser import.

Decision

1. npx ruvector rvf verify-witness <file.rvf>

Add a rvf verify-witness subcommand to the ruvector Node.js CLI (npm/packages/ruvector/bin/cli.js):

npx ruvector rvf verify-witness acceptance_manifest.rvf

Implementation path (ordered by preference):

Backend Mechanism Latency Availability
Native N-API @ruvector/rvf-node binding to rvf_crypto::verify_witness_chain() <1ms When native binary is installed
WASM @ruvector/rvf-wasm rvf_witness_verify() export ~5ms Always (WASM is universal)

The CLI auto-detects the best available backend (same pattern as the existing VectorDB platform detection). It loads the .rvf file, locates the first WITNESS_SEG, extracts the payload, and calls the verification function.

Output format:

Verifying witness chain: acceptance_manifest.rvf
  Segment type:  WITNESS_SEG (0x0A)
  Entry count:   147 entries (73 bytes each)
  Chain status:  INTACT -- all hashes verified (SHAKE-256)
  VERIFICATION:  PASSED

Error cases:

  Chain status:  BROKEN at entry 42 -- prev_hash mismatch
  VERIFICATION:  FAILED (exit code 1)

2. npx ruvector rvf inspect <file.rvf>

Extend the existing rvf inspect to parse and display acceptance test metadata from the META_SEG:

npx ruvector rvf inspect acceptance_manifest.rvf

Segments:
  [0] WITNESS_SEG  0x0A  10,731 bytes  (147 entries)
  [1] META_SEG     0x07   2,048 bytes  (JSON metadata)

Acceptance Test Metadata:
  Format:          rvf-acceptance-test v2
  Chain root hash: 7a3f...b2c1
  All passed:      true
  Scorecards:      3 modes (A/B/C)

3. rvlite browser SDK -- verifyWitnessChain()

Add a verifyWitnessChain() function to the rvlite SDK (npm/packages/rvlite/src/index.ts):

import { verifyWitnessChain } from 'rvlite';

// Load .rvf file (e.g., from fetch or File API)
const rvfBytes = await fetch('acceptance_manifest.rvf').then(r => r.arrayBuffer());
const result = verifyWitnessChain(new Uint8Array(rvfBytes));

console.log(result.valid);      // true
console.log(result.entryCount); // 147
console.log(result.error);      // null or error description

Implementation:

export interface WitnessVerifyResult {
  valid: boolean;
  entryCount: number;
  error: string | null;
}

export function verifyWitnessChain(rvfBytes: Uint8Array): WitnessVerifyResult {
  // 1. Parse segment header to find WITNESS_SEG
  // 2. Extract payload bytes
  // 3. Allocate WASM memory, copy payload
  // 4. Call rvf_witness_verify(ptr, len)
  // 5. Interpret result (positive = count, negative = error code)
  // 6. Free WASM memory
}

This function:

  • Requires @ruvector/rvf-wasm (already an optional peer dep in rvlite)
  • Throws a clear error if the WASM module is not available
  • Handles WASM memory allocation/deallocation internally
  • Returns a typed result object, not a raw integer

4. rvlite CLI -- rvlite verify-witness <file.rvf>

Register a verify-witness command in cli-rvf.ts alongside the existing rvf-migrate and rvf-rebuild commands:

npx rvlite verify-witness acceptance_manifest.rvf

This uses the same WASM backend as the SDK function above.

5. MCP tool -- rvf_verify_witness

Add to the ruvector MCP server (npm/packages/ruvector/bin/mcp-server.js) so Claude Code can verify acceptance test artifacts directly:

{
  "name": "rvf_verify_witness",
  "description": "Verify SHAKE-256 witness chain in an .rvf file",
  "input_schema": {
    "type": "object",
    "properties": {
      "path": { "type": "string", "description": "Path to .rvf file" }
    },
    "required": ["path"]
  }
}

Integration Surface

                          ┌────────────────────────┐
                          │  acceptance-rvf (Rust)  │
                          │  generate + verify      │
                          └──────────┬─────────────┘
                                     │ produces
                          ┌──────────▼─────────────┐
                          │  acceptance_manifest.rvf │
                          │  WITNESS_SEG + META_SEG  │
                          └──────────┬─────────────┘
                    ┌────────────────┼────────────────┐
                    │                │                 │
          ┌─────────▼──────┐ ┌──────▼───────┐ ┌──────▼──────────┐
          │ npx ruvector    │ │ npx rvlite   │ │ Browser (rvlite │
          │ rvf             │ │ verify-      │ │ SDK)            │
          │ verify-witness  │ │ witness      │ │ verifyWitness   │
          └────────┬───────┘ └──────┬───────┘ │ Chain()         │
                   │                │          └──────┬──────────┘
          ┌────────▼────────────────▼─────────────────▼──────────┐
          │              @ruvector/rvf-wasm                       │
          │  rvf_witness_verify(chain_ptr, chain_len) -> i32     │
          │  rvf_witness_count(chain_len) -> i32                 │
          └──────────────────────────────────────────────────────┘

Implementation Order

Phase Work Package Complexity
1 verifyWitnessChain() SDK function rvlite Low -- WASM call + segment parsing
2 verify-witness CLI command rvlite Low -- wraps SDK function
3 rvf verify-witness CLI subcommand ruvector Medium -- N-API fallback + WASM detection
4 rvf inspect metadata display ruvector Low -- parse META_SEG JSON
5 rvf_verify_witness MCP tool ruvector Low -- wraps CLI logic

Each phase is independently shippable. Phase 1+2 enable browser verification. Phase 3-5 enable CLI and agent verification.

Consequences

Positive

  • External developers verify .rvf acceptance tests with npx ruvector rvf verify-witness -- zero Rust toolchain required
  • Browser-based verification via rvlite SDK requires only npm install rvlite @ruvector/rvf-wasm
  • Claude Code agents can verify witness chains via MCP tool without file manipulation
  • Consistent verification path: Rust CLI, Node.js CLI, browser SDK, and WASM microkernel all use the same rvf_witness_verify implementation
  • Auto-detection prefers native N-API when available for sub-millisecond verification

Negative

  • WASM module adds ~46 KB to rvlite when @ruvector/rvf-wasm is installed
  • Segment header parsing must be duplicated in TypeScript (WASM only verifies the chain payload, not the segment framing)
  • N-API binding for verify_witness_chain does not exist yet in rvf-node -- Phase 3 requires adding it

Neutral

  • The JSON manifest verification (verify --input manifest.json) remains available via the Rust binary for users who prefer JSON over binary .rvf
  • @ruvector/rvf-wasm remains an optional peer dependency -- rvlite works without it but witness verification is unavailable