Skip to content

Repository files navigation

hyperware-crdt

Utilities for building Hyperware processes that keep a shared application state in sync across a committee of nodes using the Yrs CRDT.

The crate wraps a yrs::Doc in [CommitteeDoc] and offers:

  • automatic mirroring of Rust structs into Yrs maps and arrays via [StructCodec], so that only the fields you touch are propagated;
  • helpers to manage committee membership, separating peers from read-only subscribers;
  • convenience functions for producing and applying update payloads that flow over Hyperware’s messaging APIs.

Quick start

Add the dependency to your Cargo.toml:

[dependencies]
hyperware-crdt = { path = "../hyperware-crdt" } # adjust path/version as neededserde = { version = "1", features = ["derive"] }

Create a committee document, mutate your state, and sync it to another node:

use hyperware_crdt::{CommitteeDoc,PeerRole,StructCodec,
net::{apply_update_message, prepare_broadcast},};use hyperware_process_lib::Address;use serde::{Deserialize,Serialize};#[derive(Debug,Clone,Serialize,Deserialize,PartialEq)]structAppState{counter:u64,tags:Vec<String>,}fnmain() -> Result<(),Box<dyn std::error::Error>>{// Two committee members with the same starting state.let initial = AppState{counter:1,tags:vec!["alpha".into(),"beta".into()],};let doc_a = CommitteeDoc::<AppState>::new("inventory", initial.clone())?;let doc_b = CommitteeDoc::<AppState>::new("inventory", initial)?;// Declare the peer relationship.let peer_addr = Address::new("node-a",("worker","pkg","publisher"));
doc_a.membership().add(PeerRole::Peer,&peer_addr)?;// Mutate application state – only the changed fields will be diffed.letmut next_state = doc_a.read_state()?;
next_state.counter += 1;
next_state.tags.push("gamma".into());
doc_a.write_state(&next_state)?;// Prepare a broadcast update for peers.let broadcast = prepare_broadcast(&doc_a,vec![peer_addr.clone()]);for envelope in broadcast.into_envelopes(){// In a real process you would call hyperware_process_lib::hyperapp::send_rmp(envelope).apply_update_message(&doc_b,&envelope.update)?;}assert_eq!(doc_b.read_state()?, next_state);Ok(())}

Tests & examples

Run the crate’s test suite and the example application with:

cargo test
cargo run --example basic

Design notes

  • CommitteeDoc::empty("…") constructs a document without seeding any application state. This is handy when a peer bootstraps entirely from a received snapshot—apply encode_update_since(None) from another member, then resume exchanging incremental diffs using stored [StateVector]s.
  • StructCodec mirrors nested structs, maps, and vectors into the natural Yrs data structures, producing field-level diffs out of the box. Only the sections of the document that change produce CRDT operations, so appending to a vector emits a single insert rather than rewriting the entire collection.
  • If you need bespoke serialisation (e.g., compressing blobs, skipping fields), implement your own [StateCodec] and pass it into CommitteeDoc::with_codec.
  • Networking helpers return pure data structures, so you can unit test your code without hitting the Hyperware runtime. Use prepare_broadcast to package updates, and apply_update_message to apply an inbound message to a local CommitteeDoc.

Note on integers: JSON’s lossless range tops out at ±2^53. StructCodec will reject integers outside that range so you don’t silently lose precision when the state is serialised.

See the integration tests in tests/committee.rs for additional demonstrations.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages