High-performance, local-first decentralized database built on Rust and Iroh
GuardianDB is a decentralized, local-first database for apps that need peer-to-peer synchronization, offline-first operation, and high performance. Every node keeps a full local replica: reads and writes are local (no server round-trip), and changes converge across peers automatically over Iroh.
Powered by Iroh
- Direct, encrypted connections: Iroh's Magicsock handles NAT traversal, hole punching, and roaming (Wi-Fi ⇄ 5G without dropping). No global DHT.
- QUIC transport: one encrypted UDP socket multiplexes blobs (data), docs (state), and gossip (signals) per peer.
- Identity is the address: each peer is an Ed25519 public key (
NodeId, 32 bytes). - Range-Based Set Reconciliation (Willow): peers transfer only the diff between them, not full record lists, syncing millions of records in milliseconds.
- Gossip for real-time signals: iroh-gossip's epidemic broadcast trees fan out ephemeral messages with low latency and redundancy.
How the stores map to Iroh
- KeyValueStore / DocumentStore run on Iroh-Docs (Last-Write-Wins CRDT), syncing via Willow range-based reconciliation.
- EventLogStore keeps a causal DAG (ipfs-log lineage) for strict ordering and auditability, but with no IPFS, no JSON, and 32-byte BLAKE3 links instead of CIDs.
guardian-db/
├── guardian/ # GuardianDB facade (mod.rs) + core impl (core.rs)
├── stores/ # EventLogStore, KeyValueStore, DocumentStore, BaseStore
├── p2p/
│ ├── network/client.rs # IrohClient
│ ├── network/core/ # IrohBackend: blobs, docs, gossip, connection_pool, metrics, cache
│ ├── network/config.rs # ClientConfig / NetworkConfig / StorageConfig / GossipConfig
│ └── messaging/ # DirectChannel, OneOnOneChannel
├── access_control/ # Guardian (signature), Iroh, and Simple (open) controllers
├── log/ # CRDT log: Entry, Identity, LamportClock, ACL
├── cache/, data_store.rs, keystore.rs, db_manifest.rs, address.rs
└── odm/ # Optional ODM layer (feature = "odm")
Quick Start
[]
= "0.19"
= { = "1", = ["full"] }
use GuardianDB;
use NewGuardianDBOptions;
use IrohClient;
async
GuardianDB::new takes an IrohClient plus optional NewGuardianDBOptions. Use
IrohClient::development() for local work, or IrohClient::new(config) with a tuned
ClientConfig (see Configuration) for production.
Scaling
A common question: how does a local-first P2P database scale? GuardianDB has no central server, so you don't scale a bottleneck, you add peers and tune how they connect and sync.
Reads & writes are local. Each node answers queries from its own replica with no network round-trip, so read throughput scales linearly with the number of nodes. Writes are applied locally and propagated asynchronously.
Sync cost is proportional to the diff, not the dataset. Willow range-based reconciliation exchanges only what two peers are missing, so steady-state sync stays cheap even as total data grows. BLAKE3 + QUIC keep hashing and transport fast.
Discovery & connectivity (in ClientConfig):
enable_discovery_mdns: find peers on the same LAN automatically.enable_discovery_n0: global discovery via Pkarr/DNS (n0.computer), for peers across the internet.known_peers/config.add_known_peer(node_id): bootstrap against specific nodes.db.connect_to_peer(node_id).await?: force a direct connection and sync with one peer.
Share a node's identity so others can reach it:
let client = development.await?;
let node_id = client.id.await?.id; // an Ed25519 NodeId — share this with peers
let db = new.await?;
Tuning knobs that matter as you grow (all in ClientConfig):
| Concern | Field | Default → Production |
|---|---|---|
| Concurrent peers | network.max_peers_per_session |
100 → 1000 |
| Connection timeout / keepalive | network.connection_timeout, network.keepalive_interval |
30s / 60s → 60s / 120s |
| Blob cache | storage.max_cache_size |
100 MB → 1 GB |
| Largest object | storage.max_blob_size |
10 MB → 100 MB |
| Gossip throughput | gossip.message_buffer_size, gossip.max_topics |
1000 / 100 → 10000 / 1000 |
The connection pool does circuit breaking and load balancing across these connections automatically.
Topology guidance:
- Small groups (a handful of peers): let mDNS / n0 discovery form a full mesh — no extra setup.
- Larger or internet-spanning deployments: run one or more always-on "super peers" with
enable_discovery_n0 = trueand a fixedportas stable rendezvous/sync points, and list them in every client'sknown_peers. - Many independent workloads: partition by database name and gossip topic so unrelated peers don't sync data they don't need.
Start from a preset and adjust: ClientConfig::production() already raises peer limits,
cache sizes, and gossip buffers for you.
Optional ODM and Collection API
GuardianDB now includes an optional TypeORM/Mongoose-inspired ODM layer for applications that want higher-level modeling primitives without giving up local-first replication. The ODM sits above DocumentStore, so documents still synchronize through Iroh Docs/Willow while application code can use schemas, collections, indexes, and CRUD helpers.
Enable the Rust ODM explicitly:
[]
= { = " 0.19", = ["odm"] }
Rust model definitions
use Model;
use ;
let employees = db..await?;
employees.insert_one.await?;
Supported derive attributes include #[primary_key], #[unique], #[index], #[model(collection = "...")], #[model(timestamps)], #[model(flexible)], and schema versions. Runtime schemas are available through ModelSchema, FieldDefinition, and Collection::new for dynamic collections.
JavaScript/TypeScript SDK shape
The TypeScript ODM lives in its own package, guardiandb-odm-typescript, and exposes the collection API requested in the ODM RFC. It targets regular GuardianDB (the document/collection store) — not the PostgreSQL compatibility layer; for SQL/TypeORM access via the pgwire server use guardiandb-postgres-typeorm instead. The included process-local transport is for SDK development and tests; production Node/WASM/mobile bindings should implement GuardianTransport against the Rust/Iroh backend.
import GuardianDB from "guardiandb-odm-typescript";
import Iroh from "iroh";
const iroh = await ;
const guardiandb = await ;
console.log;
const collection = await guardiandb.;
await collection.;
const employee = await collection.;
const updatedEmployee = await collection.;
Collections support insertOne, batch insert, findOne, find, findById, and first-match update with MongoDB-style operators such as $set, $unset, and $inc.
Consistency boundary
ODM writes validate field types, primary keys, uniqueness, required/nullability rules, immutable primary keys, and strict schemas before persistence. Primary, unique, and secondary indexes are rebuilt from the current local document view and used for equality-query narrowing.
The implemented guarantees are intentionally local to a collection instance. GuardianDB remains decentralized and eventually convergent; disconnected peers can still create conflicting unique values that must be reconciled after replication. TransactionContext and ConsistencyLevel reserve the API shape for future replicated transaction coordination, while unsupported replicated transactions are rejected explicitly instead of implying global ACID semantics.
See docs/odm.md for the full design notes and caveats.
PostgreSQL Compatibility (TypeORM, psql, node-postgres)
GuardianDB now ships a PostgreSQL-compatible relational layer on top of its
document model. Standard PostgreSQL clients, psql, node-postgres, TypeORM
(type: "postgres"), DBeaver, connect over the PostgreSQL wire protocol and
run ordinary SQL (DDL, DML, joins, aggregates, transactions, migrations), with
no GuardianDB-specific client code.
NOTE Locking and other more advanced concepts in Postgres are to be supported
# Any PostgreSQL client connects with an ordinary connection string.
# (sslmode=disable: the loopback gateway does not negotiate TLS.)
import { DataSource } from "typeorm";
const ds = new DataSource({
type: "postgres", host: "127.0.0.1", port: 15432,
username: "guardian", password: "guardian", database: "app",
// or the single-string form:
// type: "postgres", url: "postgres://guardian:guardian@127.0.0.1:15432/app", ssl: false,
synchronize: true, entities: [User, Post, Org],
});
await ds.initialize(); // schema sync, migrations, repositories, QueryBuilder, transactions
This lives inside the guardian-db crate as feature-gated modules:
relational (types, catalog, storage trait), sql
(parser/planner/executor, information_schema/pg_catalog), and pgwire
(wire-protocol server). Enable the sql feature for the embedded engine,
which maps relational storage onto a replicated GuardianDB document store,
preserving the local-first / P2P model — or the pgwire feature (which
implies sql) to also build the guardian-pgwire server binary.
- Full guide & compatibility matrix:
docs/postgres-compat.md - Example TypeORM app:
examples/postgres-typeorm - Native driver:
packages/guardiandb-postgres-typeorm - Conformance tests:
tests/postgres-compat
Introducing Guardian Sentinel TUI
GuardianDB is a library. Historically every interaction happened through Rust code. Guardian Sentinel is a terminal UI that turns the database into something an operator or developer can inspect, manage, and monitor visually, without writing code, and everything created through it survives a restart.
It ships behind the sentinel feature (default builds are unaffected) and
gives you nine screens: a store dashboard, EventLog / KeyValue / Document
inspectors, an Access Control manager, a P2P replication monitor, a
network topology map, a keystore manager, an EventBus explorer, and a
blob browser, plus full write/management (create/close/drop stores, append
and CRUD, share and import stores over P2P tickets) with contextual help (?),
onboarding, and a client-side audit trail.
Two ways to run it
Embedded: the panel opens a data-dir directly. Works only on a directory no
other process is using (redb holds an exclusive per-process lock):
# Empty data-dir? Press `n` to create your first store.
Attached: run the RPC server in the process that owns the storage, then point
any number of panels at it over a socket, without contending for the redb lock
(the same pattern the pgwire gateway uses for SQL):
# Owner process - the only one that touches storage
# Panel - connects over RPC, holds no lock
The default RPC address is 127.0.0.1:15433. Inspection is decoupled from storage
through an AdminSource seam with two backends. EmbeddedSource (owns the
data-dir) and AdminClient (socket), so the panel renders identically in either
mode.
Keys to get started
| Key | Action |
|---|---|
F1–F7 |
Switch screens (Dashboard · Topology · Network · Access · Keystore · Blobs · Events) |
n |
New store wizard (on the Dashboard) · new item on inspectors |
Enter / Esc |
Open detail · go back |
s / i / y |
Share store · import from ticket · show node identity |
/ · ? · q |
Search · contextual help · quit |
Private key material is never displayed; destructive actions require
confirmation. Full guide: docs/SENTINEL_TUI.md.
Guardian Compute (Decentralized Edge Computing)
GuardianDB can also run work, not just store it. Guardian Compute lets a node delegate the execution of business logic, compiled to WebAssembly, to other nodes on the network, and a capability-aware scheduler routes each task to the peer with the most spare capacity. Results flow back through the same Iroh replication the database already uses. It turns GuardianDB into a lightweight decentralized edge-computing platform, without a central scheduler.
It ships behind the compute feature (default builds are unaffected) and reuses
the existing stack: QUIC + public-key identity, the Router's ALPN multiplexing,
iroh-blobs for content-addressed code distribution, iroh-gossip for capability
telemetry, the store EventBus for triggers, and the AccessController for
permissioning.
[]
= { = "0.19", = ["compute"] } # runtime + scheduler
# or, for Edge AI (wasi-nn / ONNX Runtime on CPU):
= { = "0.19", = ["compute-nn"] }
# or, to add GPU inference via the CUDA execution provider:
= { = "0.19", = ["compute-nn-cuda"] }
Write a task as an ordinary Rust function with the SDK, compile it to
wasm32-unknown-unknown, and publish the .wasm as a blob:
use ;
Then let the network decide where it runs:
let scheduler = backend.compute_scheduler.await?; // capability-aware
let delegated = scheduler.execute.await?; // best node, automatic failover
// or: execute_on (a specific node), execute_with_auction, map (MapReduce), execute_redundant (k-of-n)
Highlights:
- Sandboxed WASM runtime (
wasmtime, no WASI by default): every task runs under hard limits, memory ceiling, CPU budget (fuel), and wall-clock deadline, with no filesystem, network, clock, or randomness unless the executor's owner opts in. - Capability-aware scheduling with automatic failover, a Contract-Net auction for fresh bids, MapReduce fan-out, and k-of-n redundant execution with a reputation system for untrusted networks.
- Reactive triggers: run a task automatically when data lands
(
on_replicated), deduplicated across replicas via a task ledger. - Edge AI (
compute-nn): serve ONNX models to inference tasks overwasi-nn, with models distributed as blobs, model-affinity routing, and optional GPU (compute-nn-cuda). - Trust model: the sandbox protects the executor; result trust comes from running in permissioned networks or via redundant execution. Participation is reciprocal, never paid, and local policy stays sovereign.
Full guide: docs/compute.md.
Store Types
use GuardianDB;
use NewGuardianDBOptions;
use ;
use IrohClient;
async
use GuardianDB;
use NewGuardianDBOptions;
use KeyValueStore;
use IrohClient;
async
use GuardianDB;
use NewGuardianDBOptions;
use ;
use IrohClient;
use json;
async
use IrohClient;
use ClientConfig;
use AsyncReadExt;
async
Configuration
The simplest path is a preset on IrohClient. Each preset tunes networking, storage, and
gossip together:
| Preset | Discovery | Storage | Use for |
|---|---|---|---|
IrohClient::development() |
mDNS only | persistent, GC off | local dev |
IrohClient::production() |
mDNS + n0 (global) | 1 GB cache, GC on | deployments |
IrohClient::new(ClientConfig::testing()) |
none | in-memory | tests |
IrohClient::new(ClientConfig::offline()) |
none | persistent | no networking |
For full control, build a ClientConfig and pass it to IrohClient::new:
use GuardianDB;
use NewGuardianDBOptions;
use IrohClient;
use ;
use Duration;
async
Development
Prerequisites: Rust 1.95+ (edition 2024) and Git.
# Quality
# Optional ODM layer (feature-gated)
# P2P/integration tests are sensitive to ordering — run single-threaded:
# Benchmark features like queries, read operations, concurrency
# Check the TypeScript ODM SDK
# To test above MongoDB's 16 MiB BSON document limit
# Benchmark features with Typescript SSDK
# Check code quality and formatting
# Build documentation
# Development tools
Community & Support
GuardianDB is open source and welcomes contributions from anyone interested in decentralized systems, Iroh, and Rust. Join the Discord to ask questions and share what you build, and follow updates on Twitter and LinkedIn.
- Issues: bugs and feature requests: GitHub Issues
- Discussions: Q&A and design: GitHub Discussions
- Code: see CONTRIBUTING.md
Contributors
Status
GuardianDB is in active development and there will be breaking changes. Resulting issues are usually easy to fix, but there are no stability guarantees at this stage.
License
Dual-licensed under the MIT and Apache 2.0 licenses. Opening a pull request is assumed to signal agreement with these terms.
Acknowledgments
- Iroh: QUIC-based P2P data synchronization.
- ipfs-log-rs: CRDT log foundation, MIT © EQLabs. GuardianDB builds on it with significant enhancements for decentralized apps.
- Ratatui: Rust library for building terminal user interfaces, powering the Guardian Sentinel TUI.
GuardianDB - A secure, performant, and fully decentralized peer-to-peer database for the modern Web.