P2P sync for Obsidian using Loro CRDT and Iroh transport. Sync your vaults directly between devices without a central server.
- Conflict-free sync - Concurrent edits merge automatically using Loro CRDTs
- Offline-first - Full functionality without network; sync when peers connect
- No central server - Direct P2P connections via Iroh with relay fallback
- End-to-end encrypted - All data encrypted in transit
- Full history - Edit history preserved; deletions are recoverable
- Device groups - Organize devices with per-group sync policies
- Download the latest release from Releases
- Extract to your vault's
.obsidian/plugins/peervault/directory - Enable the plugin in Obsidian Settings > Community Plugins
# Clone the repository
git clone https://github.com/robcohen/peervault.git
cd peervault
# Install dependencies
bun install
# Build
bun run build
# Copy to your vault
cp -r dist/* /path/to/vault/.obsidian/plugins/peervault/- Open PeerVault settings on both devices
- On Device A: Click "Show QR Code" or copy the connection ticket
- On Device B: Scan the QR code or paste the ticket
- Accept the pairing request on Device A
Sync happens automatically when devices are connected. You can also:
- Click the status bar icon to see sync status
- Use Command Palette: "PeerVault: Sync now"
- Configure auto-sync interval in settings
┌─────────────────────────────────────────────────────────┐
│ Obsidian Plugin │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ File Watcher│ │ Plugin UI │ │ Peer Management │ │
│ └──────┬──────┘ └─────────────┘ └────────┬────────┘ │
│ │ │ │
│ ┌──────▼──────────────────────────┐ ┌─────▼───────┐ │
│ │ Loro Document Manager │ │ Iroh WASM │ │
│ │ - LoroTree for file hierarchy │ │ - Endpoint │ │
│ │ - LoroText for file content │ │ - Streams │ │
│ └──────┬──────────────────────────┘ └─────┬───────┘ │
│ │ │ │
│ ┌──────▼────────────────────────────────────▼───────┐ │
│ │ Sync Protocol │ │
│ │ - Version vector exchange │ │
│ │ - Incremental update sync │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
| Component | Location | Purpose |
|---|---|---|
DocumentManager |
src/core/document-manager.ts |
Loro document operations, file tree |
VaultSync |
src/core/vault-sync.ts |
File watcher, vault ↔ CRDT sync |
PeerManager |
src/peer/peer-manager.ts |
Peer connections, pairing |
SyncSession |
src/sync/sync-session.ts |
Sync protocol implementation |
IrohTransport |
src/transport/iroh-transport.ts |
P2P networking via WASM |
| Component | Library | Purpose |
|---|---|---|
| CRDT Engine | loro-crdt | Conflict-free data structures |
| P2P Transport | Iroh (WASM) | NAT traversal, encrypted streams |
| Encryption | TweetNaCl | End-to-end encryption |
| Plugin Host | Obsidian Plugin API | File access, UI integration |
# Install dependencies
bun install
# Development build with watch
bun run dev
# Production build
bun run build
# Type check
bun run check
# Run tests
bun test
# Lint
bun run lint
# Format code
bun run formatThe Iroh transport uses a custom WASM module:
# Build WASM (requires Rust + wasm-pack)
just wasm
# Clean WASM build
just wasm-clean
# Verify WASM has no env imports
just wasm-checkpeervault/
├── src/
│ ├── core/ # Document management, vault sync
│ ├── peer/ # Peer management, groups
│ ├── sync/ # Sync protocol, messages
│ ├── transport/ # Iroh transport layer
│ ├── ui/ # Settings, modals, status bar
│ └── utils/ # Shared utilities
├── peervault-iroh/ # Rust WASM bindings for Iroh
├── spec/ # Design specifications
└── tests/ # Test suites
# Run all tests
bun test
# Run specific test file
bun test tests/document-manager.test.ts
# Run with coverage
bun test --coverageDetailed design documents are in the /spec directory:
| Spec | Description |
|---|---|
| 00-overview | System architecture |
| 01-data-model | Loro document schemas |
| 04-sync-protocol | Sync message protocol |
| 05-transport-iroh | P2P networking |
| 10-security | Threat model, encryption |
| 15-peer-groups | Device groups |
-
Devices not finding each other: Ensure both devices have internet access. PeerVault uses relay servers for NAT traversal.
-
Slow connections: Try setting a custom relay server closer to your location in Advanced Settings.
-
Sync stuck: Use "PeerVault: Sync now" command or restart the plugin.
-
Missing files after sync: Check if the files are in excluded folders (Settings > Sync > Excluded Folders).
-
Conflicts: PeerVault auto-merges most conflicts. For complex conflicts, check the Conflicts tab in settings.
-
Corrupted state: Use "Reset Local State" in Danger Zone (this re-syncs from scratch).
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests (
bun test) - Run type check (
bun run check) - Commit with a descriptive message
- Push and open a Pull Request