File-Backed Data

Files are still a good local-first storage layer when users benefit from direct inspection, portability, or simple recovery.

workspace/
  manifest.json
  notes/
    2026-07-19.md
  transcripts/
    session-01.jsonl
  attachments/
    receipt-001.jpg
  indexes/
    search.sqlite

File Patterns

PatternUse forWatch out for
JSONsmall configs and snapshotswhole-file rewrites and merge conflicts
JSONLappend-only logs, transcripts, audit eventspartial lines after crash
Markdownuser-owned knowledge and notesweak schema, slower structured queries
Attachment foldersimages, PDFs, audio, large blobsorphan files and missing metadata
SQLite indexderived search or metadata over fileskeep rebuildable from source files

Atomic Write Pattern

Write to a temp file in the same directory, flush it, then rename over the old file.

import { rename, writeFile } from "node:fs/promises";

export async function writeJsonAtomic(path: string, value: unknown) {
  const tmp = `${path}.tmp`;
  await writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, "utf8");
  await rename(tmp, path);
}

This is not a full substitute for database transactions, but it avoids many torn-write failures for small files.

Manifest Rule

Use a manifest when files belong together:

{
  "formatVersion": 3,
  "createdAt": "2026-07-19T10:00:00Z",
  "attachments": [
    { "id": "receipt-001", "path": "attachments/receipt-001.jpg", "sha256": "..." }
  ]
}

Validate the manifest before import. Refuse partial imports unless the product has a clear recovery mode.