File store
The file store is the smallest durable OSS boundary. It writes event, edge, and commit chains into one operator-owned directory and can reopen them in a later process. It is useful for local tools, single-host services, and integration proofs; it is not a multi-writer database substitute.
Install the exact tested packages
Section titled “Install the exact tested packages”bun add @veritio/core@0.4.7 @veritio/storage@0.4.7The storage package receives its directory from your host. It does not discover paths or credentials from environment variables.
Persist, reopen, and verify
Section titled “Persist, reopen, and verify”This checked fixture creates a fresh directory, appends two events, constructs a second store instance to model a process restart, reads the records, and verifies the event, edge, and commit chains.
import { mkdtemp, rm } from 'node:fs/promises'import { tmpdir } from 'node:os'import { join } from 'node:path'import { createFileEvidenceStore } from '@veritio/storage'
/** * Proves that the published file store persists two tenant-scoped events across * independent store instances and re-verifies all three local evidence chains. */export async function persistReopenAndVerify() { const directory = await mkdtemp(join(tmpdir(), 'veritio-file-store-'))
try { const writer = createFileEvidenceStore(directory) await writer.recordEvent({ id: 'evt_invoice_created_01', occurredAt: '2026-08-09T12:00:00.000Z', actor: { type: 'service', id: 'billing_api' }, action: 'invoice.created', target: { type: 'invoice', id: 'inv_123' }, scope: { tenantId: 'org_acme', environment: 'test' }, metadata: { currency: 'USD' }, }) await writer.recordEvent({ id: 'evt_invoice_sent_01', occurredAt: '2026-08-09T12:01:00.000Z', actor: { type: 'service', id: 'billing_api' }, action: 'invoice.sent', target: { type: 'invoice', id: 'inv_123' }, scope: { tenantId: 'org_acme', environment: 'test' }, metadata: { channel: 'email' }, })
const readerAfterRestart = createFileEvidenceStore(directory) const events = await readerAfterRestart.listEvents() const verification = await readerAfterRestart.verify()
return { eventSequences: events.map((record) => record.sequence), reopenedEventCount: events.length, verification, } } finally { await rm(directory, { recursive: true }) }}
if (import.meta.main) { console.log(JSON.stringify(await persistReopenAndVerify(), null, 2))}Run it from the website checkout:
bun src/examples/storage/file-store.tsThe build executes the fixture and requires this exact output:
{ "eventSequences": [ 1, 2 ], "reopenedEventCount": 2, "verification": { "ok": true, "audit": { "ok": true }, "edges": { "ok": true }, "commits": { "ok": true } }}reopenedEventCount proves that the second store instance read the persisted records. The event sequences prove tenant-local ordering. Empty edge and commit chains are valid, so all three verifier results are ok: true.
Files and ownership
Section titled “Files and ownership”One directory represents one tenant and contains three independent JSONL chains:
| File | Contents | Sequence owner |
|---|---|---|
events.jsonl |
Canonical audit records | File store |
edges.jsonl |
Canonical evidence-edge records | File store |
commits.jsonl |
Commit envelopes binding event and edge members | File store |
The caller owns the directory, permissions, encryption at the host or disk layer, backups, restore procedure, and retention policy. The store requires scope.tenantId on every append and rejects a replay when the same derived idempotency identity carries different canonical content.
Concurrency and crash boundary
Section titled “Concurrency and crash boundary”The implementation serializes access with a lock file and writes each JSONL append through a temporary file plus rename. Use a local filesystem whose exclusive-create and rename semantics you have verified. Do not put one tenant directory behind multiple independent writers or an unproven network filesystem.
Production verification checklist
Section titled “Production verification checklist”- Restart between append and read, as the fixture does.
- Back up the whole tenant directory, not individual JSONL files at unrelated points in time.
- Restore into an isolated directory and run
verify()before accepting the restore. - Corrupt a copied line in a non-production restore and confirm verification fails closed.
- Alert on stale lock recovery and every non-
okverification result.
For concurrent service writers, continue with Postgres and Neon. For a complete host boundary, read Self-hosting.