A decentralized peer-to-peer event log that syncs across devices using Iroh and SQLite.
- Send events from your app
- Receive events synced from other peers
- Store events in SQLite
- Sync events over gossip (no central server)
- Single discovery key controls who syncs with you
- Events are deduplicated automatically
- Works offline - syncs when connected
- No configuration - just a discovery key and database path
- 280 lines of Rust
[dependencies]
xaeroflux = { git = "https://github.com/block-xaero/xaeroflux" }
tokio = { version = "1", features = ["full"] }use xaeroflux::{XaeroFlux, Event, generate_event_id};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut xf = XaeroFlux::new(
"my-app-key".to_string(),
"events.db".to_string()
).await?;
// Send an event
let now = chrono::Utc::now().timestamp() as u64;
xf.event_tx.send(Event {
id: generate_event_id("hello world", &xf.node_id, now),
payload: "hello world".to_string(),
source: xf.node_id.clone(),
ts: now,
})?;
// Receive synced events from other peers
while let Some(event) = xf.event_rx.recv().await {
println!("Received: {} from {}", event.payload, event.source);
}
Ok(())
}pub struct Event {
pub id: String, // Unique ID (blake3 hash)
pub payload: String, // Your data (any string)
pub source: String, // Node ID that created event
pub ts: u64, // Unix timestamp
}- You send an event via
event_tx - XaeroFlux stores it in SQLite
- XaeroFlux broadcasts it on gossip topic
xsp-1.0/{discovery_key}/events - Other peers with same discovery key receive and store it
- You receive synced events via
event_rx
Events from your own node are not sent back to event_rx (you already have them).
The discovery key determines which peers sync with each other:
// These sync together (same discovery key)
let peer1 = XaeroFlux::new("project-alpha".to_string(), "db1.db".to_string()).await?;
let peer2 = XaeroFlux::new("project-alpha".to_string(), "db2.db".to_string()).await?;
// This syncs separately (different discovery key)
let peer3 = XaeroFlux::new("project-beta".to_string(), "db3.db".to_string()).await?;cargo build --release --bin xaeroflux-cli./target/release/xaeroflux-cli \
--discovery-key demo \
--db peer1.db \
send "Hello from peer 1"./target/release/xaeroflux-cli \
--discovery-key demo \
--db peer2.db \
recvThe receiver will print events sent by the first peer (and any other peers on the same discovery key).
# Terminal 1
./target/release/xaeroflux-cli -k demo -d peer1.db send "Event 1"
./target/release/xaeroflux-cli -k demo -d peer1.db send "Event 2"
./target/release/xaeroflux-cli -k demo -d peer1.db send "Event 3"./target/release/xaeroflux-cli -k demo -d peer1.db interactiveType messages and press Enter. Type quit to exit.
use xaeroflux::{XaeroFlux, Event, generate_event_id};
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct ChatMessage {
user: String,
message: String,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut xf = XaeroFlux::new("my-chat".to_string(), "chat.db".to_string()).await?;
let node_id = xf.node_id.clone();
// Send chat messages
let tx = xf.event_tx.clone();
tokio::spawn(async move {
let stdin = tokio::io::stdin();
let mut lines = tokio::io::BufReader::new(stdin).lines();
while let Ok(Some(line)) = lines.next_line().await {
let msg = ChatMessage {
user: node_id[..8].to_string(),
message: line,
};
let payload = serde_json::to_string(&msg).unwrap();
let now = chrono::Utc::now().timestamp() as u64;
tx.send(Event {
id: generate_event_id(&payload, &node_id, now),
payload,
source: node_id.clone(),
ts: now,
}).unwrap();
}
});
// Receive and display messages
while let Some(event) = xf.event_rx.recv().await {
let msg: ChatMessage = serde_json::from_str(&event.payload)?;
println!("[{}] {}", msg.user, msg.message);
}
Ok(())
}- No snapshot sync - only events broadcast after joining
- No compaction - events stored forever
- No authentication - discovery key is only access control -- See XaeroID for this.
- No ordering guarantees beyond timestamps
- Collaborative apps (docs, whiteboards, spreadsheets)
- Chat applications
- Activity feeds
- Multiplayer game state sync
- IoT sensor data collection
- Distributed logging
Topic: xsp-1.0/{discovery_key}/events
Transport: Iroh gossip
Format: JSON-serialized Event struct
MPL v2.0