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.

Beta It works! But exercise caution in production
Explore

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 Monomorphizations 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?;
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 • HTTP/REST • Authentication • Query routing

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_org_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>
  );
});
More Examples →

Ready to Start?

Get up and running quickly with our React + Sled template

cargo generate https://github.com/ankurah/react-sled-template
View Full Guide →