ruflo

💾 RVF Storage

/docs/storage-rvf-ruvector-format/rvf-storage

Why RVF?

Previous versions shipped sql.js (18MB WASM blob) for persistent storage. This caused slow cold starts, large installs, and compatibility issues on ARM/Alpine. RVF eliminates all of that:

Before (sql.js)After (RVF)
Install size+18MB WASM0 extra deps
Cold start~2s (WASM compile)<50ms
Platform supportx86/ARM issuesRuns everywhere
Native depsOptional hnswlib-nodePure TypeScript fallback

How it works

RVF files use a simple binary layout: a 4-byte magic header (RVF\0), a JSON metadata section, then packed entries. Each module has its own format variant:

FormatMagic BytesUsed ByPurpose
RVF\00x52564600Memory backendEntries + HNSW index
RVEC0x52564543Embedding cacheCached vectors with LRU eviction
RVFL0x5256464CEvent logAppend-only domain events
RVLSLearning storeSONA patterns + trajectories

Storage auto-selection

You don't need to pick a backend. The DatabaseProvider tries each option in order and uses the first one available:

RVF (pure TypeScript) → better-sqlite3 (native) → sql.js (WASM) → JSON (fallback)

RVF is always available since it has zero dependencies, so it wins by default. If you have better-sqlite3 installed (e.g., for advanced queries), it gets priority.

Vector search with HnswLite

RVF includes HnswLite — a pure TypeScript implementation of the HNSW (Hierarchical Navigable Small World) algorithm for fast nearest-neighbor search. It's used automatically when storing entries with embeddings.

typescript
import { RvfBackend } from '@claude-flow/memory';

const backend = new RvfBackend({ databasePath: './memory.rvf' });
await backend.initialize();

// Store entries — embeddings are indexed automatically
await backend.store({ id: '1', key: 'auth-pattern', content: '...', embedding: vector });

// Search by similarity
const results = await backend.search({ embedding: queryVector, limit: 10 });

Supports cosine, dot product, and Euclidean distance metrics. For large datasets (100K+ entries), install hnswlib-node for the native implementation — the backend switches automatically.

Migrating from older formats

The RvfMigrator converts between JSON files, SQLite databases, and RVF:

typescript
import { RvfMigrator } from '@claude-flow/memory';

// Auto-detect format and migrate
await RvfMigrator.autoMigrate('./old-memory.db', './memory.rvf');

// Or be explicit
await RvfMigrator.fromJsonFile('./backup.json', './memory.rvf');
await RvfMigrator.fromSqlite('./legacy.db', './memory.rvf');

// Export back to JSON for inspection
await RvfMigrator.toJsonFile('./memory.rvf', './export.json');

Format detection works by reading the first few bytes of the file — no file extension guessing.

Crash safety

All write operations use atomic writes: data goes to a temporary file first, then a single rename() call swaps it into place. If the process crashes mid-write, the old file stays intact.

  • Memory backend: file.rvf.tmpfile.rvf
  • Embedding cache: file.rvec.tmp.{random}file.rvec
  • Event log: Append-only (no overwrite needed)

SONA learning persistence

The PersistentSonaCoordinator stores learning patterns and trajectories in RVF format, so agents retain knowledge across sessions:

typescript
import { PersistentSonaCoordinator } from '@claude-flow/memory';

const sona = new PersistentSonaCoordinator({
  storePath: './data/sona-learning.rvls',
});
await sona.initialize();

// Patterns survive restarts
const similar = sona.findSimilarPatterns(embedding, 5);
sona.storePattern('routing', embedding);
await sona.shutdown(); // persists to disk

Security

RVF validates inputs at every boundary:

  • Path validation — null bytes and traversal attempts are rejected
  • Header validation — corrupted files are detected before parsing
  • Payload limits — event log entries cap at 100MB to prevent memory exhaustion
  • Dimension validation — embedding dimensions must be between 1 and 10,000
  • Concurrent write protection — a lock flag prevents overlapping disk flushes

Configuration

bash
# Environment variables
CLAUDE_FLOW_MEMORY_BACKEND=hybrid   # auto-selects RVF
CLAUDE_FLOW_MEMORY_PATH=./data/memory

# Or via CLI
ruflo memory init --force
ruflo config set memory.backend hybrid