Cross-platform database synchronization infrastructure
Cross-platform database synchronization infrastructure with P2P networking, CRDT conflict resolution, and offline-first architecture.
Ahenk is a batteries-included Rust library that provides everything you need to synchronize databases across devices in your application ecosystem. It handles the complex distributed systems challenges so you can focus on building your app.
- 🔄 P2P Synchronization - Built on libp2p with mDNS discovery, relay support, and NAT traversal
- 🎯 CRDT Conflict Resolution - Hybrid Logical Clocks ensure causal ordering and conflict-free merges
- 📴 Offline-First - Operation log tracks all changes for synchronization when devices reconnect
- 🔐 User Authentication - Argon2 password hashing with timing-safe verification
- 📱 Device Management - Secure device pairing with QR code-based authorization
- 🚀 Zero Configuration - Automatic schema migrations and peer discovery
- 🌍 Cross-Platform - Works on iOS, Android, macOS, Windows, Linux, and embedded devices
- Rust Nightly (2024 edition required)
- SQLite 3.x
# Install Rust nightly
rustup install nightly
rustup default nightlyAdd to your Cargo.toml:
[dependencies]
ahenk = { git = "https://github.com/Appaholics/Ahenk" }
# Or from crates.io (when published):
# ahenk = "0.1"use ahenk::{initialize_database, register_user, add_device_to_user};
// Initialize database (auto-migrates schema)
let conn = initialize_database("app.db")?;
// Register a user
let user = register_user(
&conn,
"alice".to_string(),
"[email protected]".to_string(),
"secure_password".to_string(),
)?;
// Register a device
let device = add_device_to_user(
&conn,
user.user_id,
"ios".to_string(),
None,
)?;
println!("User {} registered on device {}", user.user_name, device.device_id);┌─────────────────────────────────────────────────────────────┐
│ Your Application │
│ (Tables, Business Logic) │
└────────────────────────┬────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────┐
│ Ahenk API │
├─────────────────────────────────────────────────────────────┤
│ ┌────────────┐ ┌─────────────┐ ┌───────────────────┐ │
│ │ Users │ │ Devices │ │ Operation Log │ │
│ │ & Auth │ │ Management │ │ (CRDT Oplog) │ │
│ └────────────┘ └─────────────┘ └───────────────────┘ │
│ │
│ ┌────────────┐ ┌─────────────┐ ┌───────────────────┐ │
│ │ P2P Sync │ │ CRDT │ │ Device Auth │ │
│ │ (libp2p) │ │ (HLC) │ │ (QR Pairing) │ │
│ └────────────┘ └─────────────┘ └───────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────────────┐
│ SQLite Database │
│ ┌─────────────┐ ┌───────────────┐ ┌──────────────┐ │
│ │ users │ │ devices │ │ oplog │ │
│ └─────────────┘ └───────────────┘ └──────────────┘ │
│ ┌─────────────┐ Your App Tables │
│ │ peers │ ..................... │
│ └─────────────┘ ..................... │
└─────────────────────────────────────────────────────────────┘
- Your app creates/updates data in your own tables
- Records operations in the oplog using
build_oplog_entry() - P2P sync automatically exchanges oplogs between devices
- CRDT merge applies remote operations with conflict resolution
- Your app handles table-specific updates based on oplog entries
use ahenk::initialize_database;
let conn = initialize_database("app.db")?;
// Creates users, devices, oplog, and peers tables automaticallyuse ahenk::{register_user, add_device_to_user, login_user};
// Register user
let user = register_user(&conn, username, email, password)?;
// Login
let user = login_user(&conn, username_or_email, password)?;
// Add device
let device = add_device_to_user(&conn, user.user_id, "android", None)?;use ahenk::{build_oplog_entry, local_apply};
// Your app creates a record
conn.execute(
"INSERT INTO my_app_table (id, value) VALUES (?1, ?2)",
params![id, value],
)?;
// Record the operation for sync
let entry = build_oplog_entry(
device_id,
"my_app_table",
"create",
&serde_json::json!({"id": id, "value": value}),
)?;
local_apply(&mut conn, &entry)?;use ahenk::{create_swarm, P2PConfig, SyncMessage};
// Create P2P swarm with auto-discovery
let mut swarm = create_swarm().await?;
// Or with custom config
let config = P2PConfig {
enable_mdns: true,
enable_relay: true,
relay_servers: vec!["relay.example.com".to_string()],
..Default::default()
};
let mut swarm = create_swarm_with_config(config).await?;
// Handle incoming sync messages
while let Some(event) = swarm.select_next_some().await {
match event {
SwarmEvent::Behaviour(event) => {
// Handle sync messages, merge operations, etc.
}
_ => {}
}
}use ahenk::{merge, OplogEntry, HybridLogicalClock};
// Receive operations from peer
let remote_ops: Vec<OplogEntry> = get_from_peer();
// Merge into oplog
merge(&mut conn, &remote_ops)?;
// Apply to your tables with your conflict resolution strategy
for op in remote_ops {
match op.table.as_str() {
"my_app_table" => {
// Your app-specific logic
// Use op.timestamp (HLC) for last-write-wins or custom logic
apply_my_app_table_op(&conn, &op)?;
}
_ => {}
}
}Ahenk includes a CLI for managing the sync daemon:
# Install CLI
cargo install --path . --features cli
# Initialize with user
ahenk-cli init --user alice --email [email protected]
# Start sync daemon
ahenk-cli start --daemon
# Check status
ahenk-cli status
# Manage devices
ahenk-cli device list
ahenk-cli device add --type ios
# Manage peers
ahenk-cli peer list
ahenk-cli peer add /ip4/192.168.1.100/tcp/4001/p2p/12D3...
# View logs
ahenk-cli logs --followSee docs/CLI_USAGE.md for complete CLI documentation.
use ahenk::{HybridLogicalClock, OplogEntry};
// Use HLC for causal ordering
let hlc = HybridLogicalClock::now();
// Create operation with causal timestamp
let entry = OplogEntry {
id: Uuid::new_v4(),
device_id,
timestamp: hlc.to_timestamp(),
table: "my_table".to_string(),
op_type: "update".to_string(),
data: serde_json::to_value(&my_data)?,
};use ahenk::{DeviceAuthManager, AuthorizerWorkflow, NewDeviceWorkflow};
// On device with account (authorizer)
let manager = DeviceAuthManager::new(&conn, user_id, device_id);
let mut workflow = AuthorizerWorkflow::new();
// Generate QR code
let challenge = workflow.create_challenge()?;
let qr_code = workflow.get_qr_code_string()?;
display_qr_code(&qr_code); // Show to user
// On new device
let mut new_workflow = NewDeviceWorkflow::new();
let scanned_challenge = scan_qr_code()?;
let response = new_workflow.respond_to_challenge(&scanned_challenge, device_id)?;
// Complete authorization
workflow.verify_response(&response)?;# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Test specific module
cargo test --lib --test integration_test| Platform | Architectures | Status |
|---|---|---|
| iOS | arm64, x86_64-sim | ✅ Tested |
| Android | arm64-v8a, armeabi-v7a, x86_64 | ✅ Tested |
| macOS | arm64 (M1/M2/M3), x86_64 | ✅ Tested |
| Windows | x64, arm64 | ✅ Tested |
| Linux | x86_64, arm64 | ✅ Tested |
| WatchOS | arm64, arm64-sim | ⚙️ Supported |
| WearOS | arm64-v8a, armeabi-v7a | ⚙️ Supported |
- Binary size: Optimized with LTO and size optimization
- Network efficiency: Only syncs changes since last sync (incremental)
- Memory efficient: Streaming oplog processing
- Offline capable: All operations work offline, sync when connected
- Password hashing: Argon2 with cryptographic salts
- Timing-safe comparison: Constant-time password verification
- SQL injection prevention: Parameterized queries only
- Encrypted transport: TLS/Noise protocol for P2P communication
- Device authorization: Challenge-response authentication
- UUID primary keys: Prevents enumeration attacks
| Document | Description |
|---|---|
| CLI_USAGE.md | Complete CLI tool guide |
| DATABASE_MIGRATIONS.md | Migration system guide |
| API Documentation | Complete API reference |
cargo doc --openContributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Write tests for new functionality
- Ensure all tests pass:
cargo test - Format code:
cargo fmt - Check for issues:
cargo clippy - Submit a pull request
This project uses semantic versioning:
- 0.1.0: Initial development release
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions.
Built with:
- Rust - Systems programming language
- SQLite - Embedded database
- rusqlite - Rust SQLite bindings
- libp2p - P2P networking library
- Argon2 - Password hashing
- Chrono - Date and time library
- UUID - Unique identifiers
- Serde - Serialization framework
Database synchronization made simple.