What is RURAL

Every shape of data. One living engine.

Most stacks grow a monoculture of products: a database for rows, another for documents, another for cache, another for events. RURAL is a polyculture. Many shapes of data grow in one soil, with one transaction model, one journal and one authority check.

This is the aim. Below, every shape shows what it aims to be and what it does today, with its limits.

One root system, eight plantsIllustrative
One root system feeding eight different plantsAn illustration of one engine as a shared root system. Eight plants grow from it, one for each shape of data: relational, document, graph, exact numeric, in-memory, history and branches, intake and analytics, agent access.RelationalDocumentGraphExactnumericIn-memoryHistory /branchesIntake /analyticsAgentaccessONE ENGINEtransactions · journal · authorityILLUSTRATIVE · NOT A PRODUCT DIAGRAM
Each plant is a shape of data; the roots are the shared engine. The picture is a metaphor, and the flowing dots are decorative.
One soil

Why one engine beats a stack of databases

Every extra product adds a boundary where data goes stale, permissions drift and history splits. Fewer boundaries means fewer places to lose the thread.

One transaction model

A write is prepared, journalled with an OS sync, published, then receipted. Documents, rows, edges and counters all take the same path.

One history

Journal, receipts and branches cover every shape at once. One snapshot sees a consistent view of all of it, and one fork copies all of it.

One authority check

Data cannot grant itself authority. Permission is rechecked at delivery and at commit, whatever the shape of the data. Two TLA+ models check this.

No glue between products

No sync job from the relational store to the search index, no cache to invalidate, no second backup story. One first-party engine, no third-party database underneath.

The shared semantics are real; the guarantees are not identical everywhere. Each execution profile declares its own, as set out under Profiles below.

The shapes

Nine shapes, one engine

Each row has a reference point (a tool we respect, not a claim to match it), what runs today with its limits, and what we aim for. Names of other products are reference points only.

01

Relational

Reference point: the relational depth people trust in PostgreSQL.

Typed schemas, a constrained SQL frontend and exact aggregates, running on the same snapshots as every other shape.

Today
  • Typed, nested schemas with semantic roles. Unknown critical semantics block mutation.
  • Constrained SELECT with WHERE, projection, ORDER and LIMIT. Filtered COUNT, exact signed 64-bit SUM, one-key grouped COUNT.
  • Owner-only typed inner equality join on one snapshot. Maintained unique-equality index.
  • Published 2026-09-23 local run, 4,096 records: filter + sort of 20 took 28.632 ms (p95 median), slower than SQLite and SurrealDB in the same run. A sorted query at 5,628 records was refused with request_budget_exhausted, and we published that too.

Not yet: SQL JOIN, IN, DML, arbitrary expressions, range or compound indexes, a cost planner, Postgres wire protocol or drivers. Reads audit the pinned root; there is no sublinear-planner claim.

Aim

We aim for PostgreSQL-grade relational depth: ordered and range indexes, prepared plans, EXPLAIN, and a real SQL surface over the shared model.

Illustrative request, not a specification
-- illustrative: the bounded subset, not a full dialect
SELECT id, title
FROM   tasks
WHERE  status = 'open'
ORDER BY id LIMIT 20;
02

Embedded

Reference point: the embedded simplicity of SQLite.

The same engine, linked into your program. No listener, no second process to run.

Today
  • Trusted-local Zig API: open, snapshot, query, transact, backup. The caller owns the directory and the allocator.
  • Directory-backed store with journal, owner lock and checkpoints. Offline compaction and checkpoint-based backup.
  • Bounded paged-storage proof: 4 MiB source values plus a tiny indexed collection stayed under a 1 MiB accounted working peak. A resource case, not a speed result.
  • The same engine also sits behind a loopback HTTP service. Other processes talk to the owner.

Not yet: a single-file edition (it is proposed, the active store is a directory today), power-loss certification (the journal uses OS sync, not macOS F_FULLFSYNC), and any phone or mobile engine target.

Aim

We aim for a single main-file edition and a footprint small enough to carry on constrained devices, without giving up the shared transaction model.

Illustrative request, not a specification
// illustrative: embedded use, Zig-flavoured pseudocode
const db = try rural.open(dir, allocator);
const snap = db.snapshot();
try db.transact(.{ .expected_head = snap.head, ... });
03

Document

Reference point: MongoDB- and SurrealDB-style documents.

Versioned documents with typed fields, atomic batches and retry-safe receipts.

Today
  • Create, read, replace, upsert, delete and merge-patch, schema-checked, with expected head and revision preconditions.
  • Atomic batches. A durable retry receipt is bound to the payload hash, so a replay returns the original result.
  • Versioned envelope with a 64 KiB encoded cap. Nested typed schemas, ephemeral document sessions.
  • Bounded three-way document merge with branch lineage.

Not yet: array updates, creating missing ancestors in path updates, full-text search, or an approximate-nearest-neighbour index (only small vector experiments exist).

Aim

We aim to be as flexible as a document store, with schemas you can tighten over time and migrations that keep history intact.

Illustrative request, not a specification
// illustrative request shape
{ "op": "merge_patch",
  "key": "tasks/42",
  "expected_revision": 7,
  "patch": { "status": "done" } }
04

Graph

Reference point: Neo4j-style relationships.

Relationships are first-class records, checked on write and traversed under a budget.

Today
  • Typed directed relations with properties. Endpoints, cardinality and uniqueness are validated at commit and at merge.
  • Bounded traversal from one retained view. The local server allows 4 hops, 200 nodes and 1,000 edges. Cycles are data and are de-duplicated.
  • Published 2026-09-23 local run: graph degree 16, 64, 128 gave p95 of 2.609, 20.578 and 39.175 ms.

Not yet: a graph query language such as Cypher, distributed graphs, or graph analytics. Graph structure does not infer causation.

Aim

We aim for deep, indexed traversal and a graph language, with embeddings kept as separate approximate operators rather than mixed into graph meaning.

Illustrative request, not a specification
// illustrative traversal request
{ "from": "people/eris",
  "follow": "member_of",
  "max_hops": 4, "max_nodes": 200 }
05

Exact numeric

Reference point: TigerBeetle-grade exact accounting.

Integers that never silently round, and operations that refuse rather than overflow.

Today
  • Exact signed and unsigned 64-bit values: checked add, compare-and-set and atomic transfer, with explicit overflow failure. Values above 2^53 stay lossless.
  • Original-identity replay: a retried transfer returns the first receipt.
  • A synthetic TEST_CREDIT ledger on a control branch (up to 16 accounts and 128 postings). No real funds, local only.

RURAL is not a ledger today. There is no qualified double-entry model, no 128-bit IDs or amounts, no pending/posted/void flow. TigerBeetle is an architecture reference only, and a real ledger stays authoritative there.

Aim

We aim for TigerBeetle-grade exactness and throughput on the same engine, so money-shaped data does not need a second system.

Illustrative request, not a specification
// illustrative: checked transfer, refuses on overflow
{ "op": "transfer",
  "from": "acct/a", "to": "acct/b",
  "amount": "9007199254740993" }
06

In-memory

Reference point: Redis-style speed in RAM.

A RAM profile with the same typed semantics, for state that is hot or throwaway.

Today
  • Numeric compare-and-set with retry, RAM-only expiring values, and logical-image transfer between native and RAM stores.
  • Typed maintenance and branch custody on RAM, checked by small fixtures.
  • In the browser, the RAM numeric workflow is ephemeral by design.

Not yet: Redis lists, sets, sorted sets or streams, the RESP protocol, or a universal durable TTL. Browser persistence is unqualified.

Aim

We aim for Redis-class latency on hot data, with an explicit durability class: scratch, reconstructible, or acknowledged durable.

Illustrative request, not a specification
// illustrative: RAM-only expiring counter
{ "op": "cas", "key": "rate/ip/9",
  "expect": 4, "set": 5,
  "ttl_ms": 60000 }
07

History and branches

Reference point: Git-like branches for data.

Every state is a root you can keep. Forking is cheap because the past is shared.

Today
  • Persistent immutable map: retaining a root is constant work, and a write copies only the path it touches.
  • Fork at an expected head. Parent, child and grandchild stay isolated, and dropping a parent keeps its descendants valid.
  • Bounded three-way merge with a target-head check. Randomised tests ran 2,400 operations over 12 snapshots against a model.
  • Receipts and a journal record what happened and in what order.

Git plus database is not one atomic transaction, and workspace-level coordination is design only. Branches keep history, not revoked permissions.

Aim

We aim for worlds that bundle a Git ref, a data branch and a runtime, so a whole project can be forked, tried and merged.

Illustrative request, not a specification
// illustrative: fork, write, merge
{ "op": "fork", "from": "main",
  "expected_head": 41, "name": "experiment" }
08

Intake and analytics

Reference point: ClickHouse-style event intake.

Events are accepted once, acknowledged only after local durability, and tracked through three distinct positions.

Today
  • Registered-source intake with stable-identity de-duplication, and acknowledgement only after local durability.
  • Receipt, query-visible and warehouse positions are kept separate, so late data and corrections stay honest.
  • Published 2026-09-23 local run: accepted events tracked the offered rate up to 2,048 per second (24,576 events at the top rate). This does not establish sustained capacity.

Not yet: columnar or vectorised scans, disk-scale analytics, compaction and retention at scale, or any viral-burst guarantee. Server-side forms and intake are a roadmap stage.

Aim

We aim for ClickHouse-style analytics over immutable segments and maintained projections, fed by the same durable intake.

Illustrative request, not a specification
// illustrative: one event, retry-safe
{ "source": "site", "event_id": "e-0041",
  "type": "signup", "tag": "r17" }
09

Agent access

Reference point: MCP-style access for AI agents.

A narrow, read-only door for models. The model stays outside the database.

Today
  • rural mcp exposes a read-only query tool and process-local change polling, checked by 187 packaged tests.
  • Scoped access, cursor denial, overflow refusal and fail-closed restarts.
  • Data cannot grant itself authority, and current permission is rechecked at delivery and at commit.
  • One measurement: 20 records came to 15,269 bytes in full and 4,663 bytes projected (69.5% fewer). It is projection, not understanding, and no model was called.

Not yet: published catalog, context and expand tools, or a transactions tool under a write credential (schemas are frozen, not shipped). No token-saving percentage is claimed.

Aim

We aim for agents that can read, propose and act within mandates you set, with every step on the record.

Illustrative request, not a specification
// illustrative tool call
{ "tool": "query",
  "collection": "signups",
  "where": { "tag": "r17" }, "limit": 100 }
Profiles

One engine across the whole range

The same engine is meant to run from a throwaway RAM scratchpad to a replicated cluster. Each profile states its own guarantees (ADR-017, ADR-024). Honest status below.

ProfileWhat it isStatusWhere it stands
Transient RAMScratch and hot state in memory.TodayBounded RAM profile with typed semantics. Ephemeral by design.
Embedded durable localThe engine linked into a program, with a journal on disk.TodayTrusted-local Zig API and a loopback service. Directory store, backup, offline compaction.
Browser and WASMA constrained profile in a worker.In developmentThe Aquarium demo runs the real engine as 3.4 MB of WebAssembly. Browser persistence and phones are unqualified.
Dedicated server nodeOne owning process on a server, reachable over a network.DesignLoopback-only today. A single server is a roadmap stage, and hosted launch is blocked on open gates.
Disconnected mergeable peerA cell that works offline and merges later.DesignPortable cells are exploratory. Identity and transfer have bounded TLA+ models, no runtime.
Replicated clusterCells replicated across independently operated nodes.AimNothing distributed is implemented. ADR-024 is proposed, and the replication model is safety-only with fixed membership.

We aim for nodes from a phone to a dedicated server. No phone or mobile engine target exists today, and survival claims such as multi-node resilience are targets that need matched evidence. Statuses follow ADR-017 and the proposed ADR-024.

Next

See it move, then see how it holds up.

The playground is a browser-only simulation. The architecture page shows how the pieces fit and where the limits are.