Variance is a private, decentralized messaging app for people who think their conversations are nobody else's business. No accounts, no phone numbers, no company holding your data. You generate an identity that belongs to you, pick a username, and start talking. The encryption, the peer discovery, the relay infrastructure — all of it happens invisibly. You shouldn't have to understand cryptography to deserve privacy.
Conversations in Variance are ephemeral by design. Messages fade after 30 days, because that's how real conversations work — you remember what matters and let the rest go. There are no servers to subpoena, no message archives to leak, and no social graph being harvested in the background. Find your people however you find them: share a username in person, send a link, scan a code. Variance doesn't want to know who your friends are.
Variance is a multi-crate workspace implementing a decentralized communication platform with:
- End-to-end encrypted messaging (1-on-1 and groups)
- WebRTC audio/video calls with NAT traversal
- Decentralized identity using W3C DIDs and IPFS/IPNS
- No central servers (optional relay/TURN infrastructure)
variance/
├── crates/
│ ├── variance-proto/ # Protocol Buffer definitions
│ ├── variance-p2p/ # libp2p networking core
│ ├── variance-identity/ # DID-based identity system
│ ├── variance-messaging/ # Chat and messaging
│ ├── variance-media/ # WebRTC media handling
│ ├── variance-app/ # Application logic & HTTP API
│ ├── variance-relay/ # Standalone relay node (NAT traversal, offline message relay)
│ └── variance-cli/ # Standalone CLI (headless/debugging only)
├── app/ # Tauri desktop application (React/TypeScript)
│ ├── src/ # UI components (onboarding, conversations, messages)
│ └── src-tauri/ # Tauri host (embeds variance_app in-process via FFI)
├── docs/ # Architecture documentation
└── Cargo.toml # Workspace manifest
| Crate | Purpose | Key Dependencies |
|---|---|---|
variance-proto |
Protobuf schemas | prost |
variance-p2p |
libp2p networking + protocol handlers | libp2p, tokio, variance-{identity,messaging,media} |
variance-identity |
DID & identity logic | variance-proto, ed25519-dalek |
variance-messaging |
Messaging logic | variance-proto, variance-identity, ulid |
variance-media |
WebRTC signaling logic | variance-proto, ed25519-dalek |
variance-app |
HTTP API & state | axum, variance-p2p, variance-{identity,messaging,media} |
variance-relay |
Standalone relay node (NAT traversal) | libp2p, axum, clap |
variance-cli |
Standalone CLI (headless/debugging) | variance-app, clap |
variance-desktop |
Tauri desktop host (primary runtime) | tauri, variance-app |
Architecture note: The Tauri desktop app embeds variance-app in-process — there is no sidecar. The React frontend talks to the Rust node directly via Tauri commands (FFI). variance-cli exists for headless operation, debugging, and testing only.
Note: As of 2026-02-15, the dependency flow was corrected. variance-p2p now depends on the business logic crates (not vice versa) to wire up protocol handlers.
- Language: Rust 2021 edition
- P2P: libp2p 0.55 (Kademlia DHT, GossipSub, custom protocols)
- Async: Tokio 1.42
- HTTP: Axum 0.8
- Serialization: Protocol Buffers (prost)
- Tracing: tracing + tracing-subscriber
- Errors: snafu
- Crypto: ed25519-dalek, vodozemac 0.9 (Olm/Double Ratchet for DMs), OpenMLS / RFC 9420 (group messages)
- Storage: sled (embedded KV store)
- Desktop: Tauri 2.x (React/TypeScript frontend)
- Identity: BIP39 (mnemonic recovery), IPFS/IPNS (storage)
- IDs: ULID (messages, calls), chrono (timestamps)
- Latest stable Rust (2021 edition)
- Protocol Buffers compiler (
protoc)
macOS:
brew install protobufLinux:
apt install -y protobuf-compiler # Debian/Ubuntu
dnf install -y protobuf-compiler # FedoraWindows:
choco install protoc# Build all crates
cargo build
# Build specific crate
cargo build -p variance-app
# Build release
cargo build --releaseThe primary way to use Variance is through the Tauri desktop app, which embeds the node in-process.
Using the justfile (recommended):
just dev # Run the desktop app in dev mode
just tauri-build # Build a release bundle
just frontend-dev # Run just the frontend (no Tauri/node)
just dev-two # Run two instances for P2P testingOr directly via pnpm in the app/ directory:
cd app
pnpm install # First time only
pnpm run tauri:dev # Dev mode with hot reload
pnpm run tauri:build # Release bundle
pnpm run dev # Frontend only (Vite dev server, no Tauri)The desktop app handles identity generation, configuration, and node startup automatically through its onboarding flow.
The variance CLI exists for headless operation, debugging, and testing (e.g. generating identities without the UI, running a second node for P2P testing):
# Generate an identity
cargo run --bin variance -- identity generate
# Start a headless node
cargo run --bin variance -- start
# Show identity info
cargo run --bin variance -- identity showSee docs/CLI-USAGE.md for the full command reference.
A relay node enables NAT traversal for peers behind restrictive firewalls. It accepts circuit reservations and forwards traffic between peers that cannot connect directly.
# Build the relay binary
cargo build -p variance-relay --release
# Run (listens on port 4001 by default)
./target/release/variance-relay --port 4001The relay prints its PeerId on startup:
INFO variance_relay: Relay PeerId: 12D3KooW...
INFO variance_relay: Listening on /ip4/0.0.0.0/tcp/4001
To configure clients to use the relay, add to your config.toml:
[[p2p.relay_peers]]
peer_id = "12D3KooW..."
multiaddr = "/ip4/<YOUR_IP>/tcp/4001"The client will dial the relay on startup, reserve a circuit slot, and become reachable at /ip4/<YOUR_IP>/tcp/4001/p2p/12D3KooW.../p2p-circuit/p2p/<CLIENT_PEER_ID>.
See docs/NAT-TRAVERSAL.md for design details.
cargo testThis project uses pre-commit to automatically run checks before commits and pushes. The hooks ensure code quality by running formatters, linters, and tests.
Installation:
-
Install pre-commit (if not already installed):
# macOS brew install pre-commit # Linux/macOS with pip pip install pre-commit # Or use pipx pipx install pre-commit
-
Install the git hooks in your local repository:
pre-commit install pre-commit install --hook-type pre-push
What Gets Checked:
On every commit:
- Trailing whitespace removal - Cleans up line endings
- End-of-file fixer - Ensures files end with a newline
- YAML/TOML validation - Checks config files for syntax errors
- Large file detection - Prevents files >500KB from being committed
- Merge conflict markers - Catches unresolved conflicts
cargo fmt- Auto-formats Rust codecargo clippy- Lints Rust code with auto-fixes (treats warnings as errors)cargo check- Verifies code compiles
On every push:
cargo test- Runs the full test suite
Manual Usage:
# Run all hooks on all files (useful after installation)
pre-commit run --all-files
# Run only on staged files
pre-commit run
# Run specific hook
pre-commit run cargo-fmt --all-files
pre-commit run clippy --all-files
# Skip hooks for a specific commit (not recommended)
git commit --no-verifyUpdating Hooks:
# Update hook versions to latest
pre-commit autoupdateThe pre-commit configuration is in .pre-commit-config.yaml.
Protobufs are automatically compiled during build via prost-build in variance-proto/build.rs.
To manually trigger:
cargo build -p variance-protoRUST_LOG controls log verbosity for both the desktop app and the CLI — the node is the same code either way:
# Desktop app
RUST_LOG=variance=debug just dev
# CLI (headless)
RUST_LOG=variance=debug cargo run --bin variance -- start
# Trace level with libp2p internals
RUST_LOG=variance=trace,libp2p=debug just dev- DHT for peer discovery: Kademlia DHT used for provider records and peer routing
- IPFS/IPNS for identity: DID documents stored in IPFS, mutable pointers via IPNS
- Custom libp2p protocols: Direct peer queries with multi-layer caching
- Protobuf everywhere: Type-safe schemas for all P2P communication
- Local message history: Messages are encrypted at rest with keys derived from your identity file. Losing the identity file means losing message history — there is no cloud backup.
- No automatic group sync on reconnect: If you go offline and come back, group messages sent while you were away are not automatically fetched. Workaround: stay connected or request a manual sync from a group member.
- IPFS/IPNS requires a local daemon: Identity storage uses IPFS/IPNS when a local daemon is reachable (
http://127.0.0.1:5001). When unavailable, Variance falls back to local-only identity storage — peers cannot resolve your DID via IPFS, only via P2P direct query.
- docs/ - Architecture documentation
- docs/NAT-TRAVERSAL.md - Relay node and NAT traversal design
- CLI-USAGE.md - CLI reference (debugging/testing only)
Foundation:
- Workspace structure
- Protobuf schemas (identity, messaging, media)
- libp2p node setup (DHT, GossipSub, mDNS, Identify, Ping)
- Custom protocol codecs (request/response)
- Protocol handlers with event channels
Identity System:
- DID generation (ed25519 keys)
- Identity resolution protocol handler
- In-memory caching layer
- IPFS/IPNS integration for persistent storage (degrades gracefully to local fallback)
- DHT provider records for username discovery (untested)
Messaging:
- Protobuf message schemas
- Olm Double Ratchet encryption for direct messages (vodozemac 0.9)
- OpenMLS (RFC 9420) for group messages — per-message forward secrecy, post-compromise security
- Offline message relay protocol handler
- Local storage backend (sled) with ULID-sorted history
- GossipSub integration for groups
- Read receipts and delivery status
- Typing indicators
Media (WebRTC):
- Signaling protocol handler
- Offer/Answer/ICE/Control message handling
- Message signing and verification
- Call state management (Ringing → Connecting → Active → Ended)
- 🚧 WebRTC peer connection (media stream negotiation)
- 🚧 STUN/TURN configuration
Application Layer:
- HTTP API framework (axum)
- Complete REST API endpoints (identity, messages, groups, calls, signaling, receipts, typing, presence)
- Cursor-based message pagination (
?before=<ts>, newest-first storage scan) - WebSocket event delivery for Tauri frontend
- Real-time message delivery (inbound tick → component
refetch()) - Event subscription system
- CLI with identity management
- Tauri desktop app (onboarding, identity generation/recovery, conversations, messages UI)
- Infinite scroll (scroll-to-top loads older message pages via
IntersectionObserver) - Group management API (create, invite, join, leave, list members)
- Presence status endpoint
- Username resolution endpoint
Testing & Documentation:
- Unit tests for all handlers
- Integration tests for protocol flows
- Architecture documentation
- Relay Network Testing — Relay infrastructure is implemented; needs real-world multi-hop testing across NATs
- WebRTC Peer Connection — Signaling protocol is complete; wire up actual media stream negotiation and STUN/TURN server configuration
- Call UI — Tauri frontend has message/conversation UI; call screens and media controls not yet wired
- Integration Testing — IPFS/IPNS and DHT provider records need integration tests with a live daemon
AGPL-3.0 License. See LICENSE for details.
Contributions welcome. Core messaging, identity, and relay infrastructure are implemented and stable.
Key areas for future work:
- End-to-end integration testing (IPFS/IPNS, DHT provider records)
- WebRTC peer connection and STUN/TURN
- Relay node selection and failover
- Mobile support (iOS/Android)