Cryptographic Identity
Every device gets one key. Just one. Because if you need two, you're doing it wrong.
Ed25519: One Key to Rule Them All
Ed25519 gives you a 256-bit identity that's mathematically unique. The odds of collision? About the same as guessing a specific atom in the observable universe. Twice.
/// Generate a new random identity.
pub fn generate() -> Self {
let signing_key = SigningKey::generate(&mut OsRng);
Self { inner: signing_key }
}
/// Sign a message. 64 bytes of proof.
pub fn sign(&self, message: &[u8]) -> Signature {
self.inner.sign(message)
}
/// Verify: Is this really from who they claim?
pub fn verify(
&self,
message: &[u8],
signature: &Signature
) -> Result<(), IdentityError> {
self.public_key.verify(message, signature)
}
Click to generate...
seed (32 bytes) → SecretKey → PublicKey → hex(PublicKey) = PeerID
End-to-End Encryption
X25519 for the handshake. XChaCha20-Poly1305 for everything after. Because repeating the key exchange for every message would be... wet.
Key Exchange
X25519 Diffie-Hellman
alice.dh(bob.pub) == bob.dh(alice.pub)
Key Derivation
HKDF-SHA256
shared → cipher_key (32 bytes)
Encrypt
XChaCha20-Poly1305
nonce || ciphertext || tag
/// Diffie-Hellman: Same result, different inputs
pub fn diffie_hellman(
&self,
peer_public: &X25519PublicKey
) -> SharedSecret {
let shared = self.inner
.diffie_hellman(&peer_public.to_dalek());
SharedSecret {
bytes: shared.to_bytes()
}
}
/// Derive encryption key from shared secret
pub fn to_cipher_key(&self) -> SecretKey {
let key_bytes = self.derive_key(
b"kagami-mesh-xchacha",
32
);
SecretKey::from_bytes(&key_bytes)
}
/// 192-bit nonce = never repeats
pub const NONCE_SIZE: usize = 24;
/// Encrypt with random nonce prepended
pub fn encrypt(
key: &SecretKey,
plaintext: &[u8]
) -> Result<Vec<u8>> {
let nonce = Nonce::generate();
let ciphertext = cipher
.encrypt(&nonce, plaintext)?;
// nonce || ciphertext || tag
let mut result = Vec::new();
result.extend(&nonce.bytes);
result.extend(&ciphertext);
Ok(result)
}
- 192-bit nonce: Random nonces without collision risk (unlike AES-GCM's 96-bit)
- Poly1305 MAC: Authentication tag detects any tampering
- Constant-time: No timing side-channels to exploit
- No padding: Stream cipher = no padding oracle attacks
Live Encryption Demo
Generate keys first...
Not generated
Conflict-Free Synchronization
Vector Clocks track causality. CRDTs resolve conflicts automatically. Because "last write wins" is a terrible strategy when you have 6 devices.
Vector Clock
Happens-before ordering without synchronized clocks
{ "hub": 5, "phone": 3, "watch": 2 }
G-Counter
Distributed counting that only grows
sum(all_nodes) = total
LWW-Register
Last-writer-wins with timestamps
max(timestamp) = winner
OR-Set
Add and remove without conflicts
unique_tags = no conflicts
Vector Clock: Time Without Time
Wall clocks lie. Network delays exist. But Vector Clocks don't care. They track causality, not time.
/// Compare: Who happened first?
pub fn compare(&self, other: &VectorClock)
-> VectorClockOrdering
{
match (self_less, other_less) {
(false, false) => Equal,
(true, false) => HappensBefore,
(false, true) => HappensAfter,
(true, true) => Concurrent,
}
}
/// Merge: Take max of each node
pub fn merge(&mut self, other: &VectorClock) {
for (node_id, &value) in &other.clocks {
self.clocks
.entry(node_id)
.and_modify(|v| *v = max(*v, value))
.or_insert(value);
}
}
CRDTs: Math Beats Coordination
Why have a leader resolve conflicts when math can do it? CRDTs merge deterministically. Same inputs = same result. Every. Single. Time.
/// OR-Set: Add with unique tag
pub fn add(&mut self, value: T, node_id: &str) {
let tag = self.generate_tag(node_id);
self.elements.insert(OrSetElement {
value,
tag // "node1:42" - unique!
});
}
/// Remove: Only removes YOUR adds
pub fn remove(&mut self, value: &T) {
let tags: Vec<String> = self.elements
.iter()
.filter(|e| &e.value == value)
.map(|e| e.tag.clone())
.collect();
for tag in tags {
self.tombstones.insert(tag);
}
}
CRDT sync:
a.merge(b).We wrote the merge logic once. It handles all conflicts. DRY
Live Sync Demo
{ "phone": 0 }
{ "hub": 0 }
{ "phone": 0, "hub": 0 }
Mesh Topology
Hub as coordinator. Mobile as nodes. Desktop as workstation. All speaking the same protocol via UniFFI bindings.
UniFFI: Write Once, Bind Everywhere
The Rust SDK compiles to native binaries. UniFFI generates bindings. iOS, Android, Desktop - all using the same cryptographic code.
kagami-mesh-sdk (Rust)
uniffi::setup_scaffolding!()
.swift
.kt
.rs
With UniFFI: Write Ed25519 in Rust. Generate the rest. Sleep well at night.
Same cryptographic guarantees. One implementation. Zero bugs duplicated. DRY
Voice Processing
Speech recognition written once. Running everywhere. That's the whole point.
500ms. Not 500ms × 6.
Context Sync
State duplicated automatically, so you don't have to. Merge logic: written once.
∞ devices, 1 truth
Offline-First
Your network code, written once. Your users' frustration, eliminated everywhere.
1 pattern, 0 cascades
Circuit Breaker
Networks fail. The question is: do you fail gracefully or hammer a dead server?
/// Check if a request should be allowed
pub fn allow_request(&self) -> bool {
match self.state() {
CircuitState::Closed => true,
CircuitState::Open => {
// Check if reset timeout has elapsed
if time_since_failure > RESET_TIMEOUT {
self.set_state(HalfOpen);
true
} else {
false // Fail fast!
}
}
CircuitState::HalfOpen => true,
}
}
/// Pattern: Closed → Open → HalfOpen → Closed
/// h(x) >= 0. Always.
We wrote
CircuitBreaker once in Rust.iOS, Android, Desktop, watchOS, visionOS - all use the same implementation.
One circuit breaker. Six platforms. Zero repeated bugs. DRY
Live Demo
Watch the mesh in action. Every node. Every message. Every sync.
Protocol Stack Summary
| Layer | Technology | Purpose | DRY Factor |
|---|---|---|---|
| Identity | Ed25519 |
Sign & verify | 1 seed → all keys |
| Key Exchange | X25519 |
Shared secret | 1 exchange → all messages |
| Encryption | XChaCha20-Poly1305 |
Confidentiality | 1 impl → 6 platforms |
| Causality | VectorClock |
Ordering | 1 merge → ∞ nodes |
| State | CRDTs |
Consistency | 1 merge → 0 conflicts |
| Resilience | CircuitBreaker |
Graceful degradation | 1 pattern → 0 cascades |