ankurah

Crates.ioankurah
lib.rsankurah
version0.7.17
created_at2025-01-20 00:27:45.89721+00
updated_at2026-01-23 00:28:36.580724+00
descriptionObservable, event-driven state management for native and web
homepagehttps://github.com/ankurah/ankurah
repositoryhttps://github.com/ankurah/ankurah
max_upload_size
id1523373
size116,431
Daniel (dnorman)

documentation

https://docs.rs/ankurah

README

Ankurah Logo

Ankurah

The root of all cosmic projections of state

A distributed state-management framework that enables real-time data synchronization
across multiple nodes with built-in observability.

Get StartedDocumentationDiscord

Beta Status Crates.io Documentation


What is Ankurah?

Ankurah is a real-time reactive database for building collaborative and multiplayer applications. Define your data model once in Rust, and use it everywhere—native servers, browser clients, mobile apps—with automatic synchronization across all connected nodes.

The Challenge

Building applications where multiple users interact with shared state is hard. You need real-time sync, offline support, conflict resolution, reactive UI updates, and query pushdown—and it all has to work consistently across your server (Postgres) and clients (IndexedDB in browsers). Most teams either build fragile ad-hoc solutions or accept significant compromises.

The Approach

Ankurah is event-sourced: every change is an operation that can be replayed, merged, and synchronized. Entities can reference each other (graph-style navigation), and you can subscribe to live queries that automatically update as data changes anywhere in the system.

Your model is defined once in Rust with a derive macro. The same code compiles to native binaries for servers and WASM for browsers. TypeScript interfaces and React hooks are generated automatically.

Why Ankurah?

Local-first by default. Your queries execute against the local cache instantly, then sync with durable servers in the background. Users get sub-millisecond reads and offline support without you writing sync logic.

Live queries that just work. Subscribe to a query and your UI stays in sync automatically—changes flow in from anywhere.

Graph-ready data model. Entities can reference each other, and you can navigate those relationships directly in queries. Full graph traversal is on the roadmap.

Collaborative editing built in. Text fields can use Yjs-backed CRDTs for real-time collaborative editing with automatic conflict resolution.

One codebase, multiple backends. Swap between Sled, SQLite, Postgres, and IndexedDB without changing application code. Your server uses Postgres while browsers use IndexedDB—same queries, same models.

Who it's for

Teams building collaborative tools, local-first apps, multiplayer experiences, or anything where users interact with shared state in real-time.


Key Features

1️⃣ Schema-First Design

#[derive(Model, Debug, Serialize, Deserialize)]
pub struct Album {
    #[active_type(YrsString)]
    pub name: String,
    pub artist: String,
    pub year: i32,
}

YrsString is default backend for String, LWW otherwise

🔍 Live Queries

Rust

let q: LiveQuery<AlbumView> = ctx.query("year > 1985")?;

TypeScript

const q: AlbumLiveQuery = Album.query(ctx(), "year > 1985");

Views and WASM bindings generated by the Model macro

⚛️ React Support

/* creates and Binds a ReactObserver to the component */
const AlbumList = signalObserver(({ albums }: Props) => {
  return (
    <ul>
      /* React Observer automatically tracks albums */
      {albums.items.map((album) => (
        <li>{album.name}</li>
      ))}
    </ul>
  );
});

ReactObserver tracks livequery.items

🗄️ Flexible Storage

Sled

let storage = SledStorageEngine::new()?;

Postgres

let storage = Postgres::open(uri).await?;

SQLite

let storage = SqliteStorageEngine::open("myapp.db").await?;

IndexedDB

let storage = IndexedDBStorageEngine::open("myapp").await?;

TiKV planned

⚡ Generated Interfaces

export class Album {
  static query(context: Context, selection: string, ...args: any): AlbumLiveQuery;
  ...
}

🔋 Batteries Included

WebSocket server & client • HTTP/REST endpoints • Authentication hooks • Query routing & pushdown


Quick Example

Server

let storage = SledStorageEngine::with_path(storage_dir)?;
let node = Node::new_durable(Arc::new(storage), PermissiveAgent::new());
node.system.create().await?;

let mut server = WebsocketServer::new(node);
println!("Running server...");
server.run("0.0.0.0:9797").await?;

Rust Client

let storage = SledStorageEngine::new_test()?;
let node = Node::new(Arc::new(storage), PermissiveAgent::new());
let _client = WebsocketClient::new(node.clone(), "ws://localhost:9797").await?;
node.system.wait_system_ready().await;

// Create album
let ctx = node.context(ankurah::policy::DEFAULT_CONTEXT)?;
let trx = ctx.begin();
trx.create(&Album { name: "Parade".into(), artist: "Prince".into(), year: 1986 }).await?;
trx.commit().await?;

WASM Client

let storage = IndexedDBStorageEngine::open("myapp").await?;
let node = Node::new(Arc::new(storage), PermissiveAgent::new());
let _client = WebsocketClient::new(node.clone(), server_url)?;
node.system.wait_system_ready().await;

let context = node.context(DEFAULT_CONTEXT)?;
let _albums = context.query::<ankurah_doc_example_model::AlbumView>("year > 1985")?;

React Component

/* creates and Binds a ReactObserver to the component */
const AlbumList = signalObserver(({ albums }: Props) => {
  return (
    <ul>
      /* React Observer automatically tracks albums */
      {albums.items.map((album) => (
        <li>{album.name}</li>
      ))}
    </ul>
  );
});

Getting Started

Get up and running quickly with our React + Sled template:

cargo generate https://github.com/ankurah/react-sled-template

Or run the example manually:

# Start the server
cargo run -p ankurah-example-server

# Build WASM bindings (in examples/wasm-bindings)
wasm-pack build --target web --debug

# Run the React app (in examples/react-app)
bun install && bun dev

Then open http://localhost:5173/ in two browser tabs (one regular, one incognito) to see real-time sync.


Core Concepts

Concept Description
Model A struct describing fields and types for entities in a collection
Collection A group of entities of the same type (like a database table)
Entity A discrete identity in a collection with dynamic schema
View A read-only, typed representation of an entity
Mutable A mutable, typed state representation of an entity
Event An atomic change used for synchronization and audit trail

Roadmap

Milestone 2 - TiKV backend, Graph functionality, User-definable data types

Milestone 3 - P2P, Portable cryptographic identities, E2EE, Hypergraph


GitHubDiscordWebsite
Licensed under MIT or Apache-2.0
Commit count: 505

cargo fmt