Matt Hill 86b0768bbb audit fixes, repo restructure, and documentation
Soundness and performance audit (17 fixes):
- See AUDIT.md for full details and @claude comments in code

Repo restructure:
- Inline json-ptr and json-patch submodules as regular directories
- Remove cbor submodule, replace serde_cbor with ciborium
- Rename patch-db/ -> core/, patch-db-macro/ -> macro/,
  patch-db-macro-internals/ -> macro-internals/, patch-db-util/ -> util/
- Purge upstream CI/CD, bench, and release cruft from json-patch
- Remove .gitmodules

Test fixes:
- Fix proptest doesnt_crash (unique file paths, proper close/cleanup)
- Add PatchDb::close() for clean teardown

Documentation:
- Add README.md, ARCHITECTURE.md, CONTRIBUTING.md, CLAUDE.md, AUDIT.md
- Add TSDocs to TypeScript client exports

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 19:06:42 -07:00

patch-db

A database that tracks state updates as RFC 6902 JSON Patches. Enables observable, event-driven state management with a Rust backend and TypeScript client.

Overview

patch-db stores your application state as a single JSON document. Instead of opaque writes, every mutation is recorded as a JSON Patch — a sequence of add/remove/replace operations. Subscribers receive only the patches relevant to the subtree they're watching, making it efficient for UIs that need to react to fine-grained state changes.

Key properties

  • Event-sourced — patches are the source of truth, not snapshots
  • Observable — subscribers watch arbitrary subtrees via JSON Pointers and receive scoped patches in real time
  • Persistent — the Rust backend writes to disk with CBOR serialization, automatic compaction, and crash-safe backup files
  • Type-safe — derive macros on the Rust side; generic type parameters and deep watch$() overloads on the TypeScript side
  • Immutable values — the Rust side uses imbl_value::Value for efficient structural sharing

Quick start

Rust

Add to your Cargo.toml:

[dependencies]
patch-db = { git = "https://github.com/Start9Labs/patch-db" }
use patch_db::PatchDb;
use json_ptr::ROOT;

#[tokio::main]
async fn main() -> Result<(), patch_db::Error> {
    let db = PatchDb::open("my.db").await?;

    // Write a value
    db.put(&ROOT, &serde_json::json!({ "count": 0 })).await?;

    // Read it back
    let dump = db.dump(&ROOT).await;
    println!("revision {}: {}", dump.id, dump.value);

    // Subscribe to changes
    let mut watch = db.watch(ROOT.to_owned()).await;
    // watch implements Stream — use it with tokio, futures, etc.

    Ok(())
}

TypeScript

import { PatchDB, Dump, Update } from 'patch-db'
import { Observable } from 'rxjs'

interface AppState {
  users: { [id: string]: { name: string; online: boolean } }
  settings: { theme: string }
}

// source$ delivers updates from the server (WebSocket, SSE, etc.)
const source$: Observable<Update<AppState>[]> = getUpdatesFromServer()

const db = new PatchDB<AppState>(source$)
db.start()

// Watch a deeply nested path — fully type-safe
db.watch$('settings', 'theme').subscribe(theme => {
  console.log('Theme changed:', theme)
})

Further reading

  • ARCHITECTURE.md — project structure, crate/package details, data flow, storage format
  • CONTRIBUTING.md — environment setup, build commands, testing, code style

License

MIT

Description
No description provided
Readme 1.1 MiB
Languages
Rust 89.6%
TypeScript 10.4%