A decentralized peer-to-peer chat application with local-first data storage, end-to-end encryption, and permission-based content sharing.
- Product name: Harbor
- Tagline: Decentralized Chat
- Public site / invite handoff:
https://social-harbor.com - Default community relay: Harbor Community Relay
- Default relay address:
/ip4/100.49.236.191/tcp/4001/p2p/12D3KooWMfwHKfzDrZ2V3Zniw3Qu797bHrKsFKAdG9CtQiaEhbQ3
Harbor contact invites use the public site as a friendly handoff URL and embed the full harbor:// contact bundle needed by the desktop app to add/connect to a contact.
- Decentralized Identity: Ed25519 keypairs for signing, X25519 for key agreement
- Local-First: All data stored locally in SQLite, you own your data
- P2P Networking: Direct peer connections via libp2p (mDNS, Kademlia DHT, NAT traversal)
- End-to-End Encryption: AES-256-GCM with HKDF-derived conversation keys
- Permission System: Signed capability grants for content access (Chat, WallRead, Call)
- Event Sourcing: Append-only logs with lamport clocks for conflict-free sync
- One-to-One Voice Calling: signed libp2p signaling, WebRTC audio runtime, persisted call history, and configurable ICE/STUN/TURN settings; release readiness still requires the two-profile evidence in
docs/voice-call-e2e-validation.md - Wall and Feed Sync: local-first wall posts with visibility controls, media, preview/RSS/share surfaces, contact-wall/feed reads, signed comments/reactions, edit/delete reconciliation, and direct/relay sync; release readiness still requires the three-profile evidence in
docs/wall-sync-multi-profile-validation.md - Group-Call Topology Contract: first production group calls use the ADR-0001 relay-assisted small-group mesh with a hard 4-participant limit; video/group claims require
docs/video-group-call-validation.md
- Node.js (v18+)
- Rust (stable)
- Tauri Prerequisites
- Windows: Microsoft Visual Studio C++ Build Tools
- macOS: Xcode Command Line Tools
- Linux:
sudo apt install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
# Clone the repository
git clone https://github.com/Bakobiibizo/harbor.git
cd harbor
# Install frontend dependencies
pnpm install
# Run in development mode
pnpm tauri dev# Build the application
pnpm tauri build
# The executable will be in src-tauri/target/release/- When you first open Harbor, you'll be prompted to create an identity
- Enter a Display Name (how others will see you)
- Optionally add a Bio
- Create a Passphrase (at least 8 characters) - this encrypts your private keys
- Important: Store your passphrase safely! If you lose it, you cannot recover your identity
- On subsequent launches, enter your passphrase to unlock
- Your identity remains encrypted on disk when locked
- Go to the Network tab
- Click Start Network to connect to the P2P network
- Peers on your local network running Harbor will be discovered automatically via mDNS
- The status indicator shows your connection state
- In the Network tab, you'll see discovered peers
- Click the checkmark to add a peer as a contact
- You can search for peers by their Peer ID
- Use the Contacts tab to manage your contact list
- Go to the Messages tab
- Select a contact to open a conversation
- Messages are end-to-end encrypted using derived conversation keys
- Click the phone icon to initiate a voice call (if supported)
- Go to the Wall tab
- Use the composer at the top to create a post and choose Public or Contacts only visibility
- You can add images and videos to your posts
- Use Preview and share your wall to view guest/contact/owner perspectives, copy/export public-only RSS XML, copy your public feed URI, or copy a contact invite
- Posts are stored locally and shared with contacts who have permission
RSS XML is generated locally from public posts only; Harbor does not currently host RSS over HTTP. See Wall preview, RSS, and share surfaces for the exact visibility behavior.
- Go to the Feed tab
- See posts from contacts whose public posts are available or who have granted you WallRead permission for contacts-only posts
- Like/react, comment, save, hide, snooze, and share through the production feed/contact-wall surfaces where the current build exposes those controls; relay and multi-profile release evidence is tracked in
docs/wall-sync-multi-profile-validation.md
Access settings to:
- Profile: Update your display name, bio, and avatar
- Security: Change passphrase, export/import identity
- Network: Configure auto-start and mDNS discovery
- Privacy: Control post visibility and read receipts
src/
├── components/
│ ├── common/ # Button, Input, etc.
│ ├── icons/ # SVG icon components
│ ├── layout/ # MainLayout with sidebar
│ └── onboarding/ # CreateIdentity, UnlockIdentity
├── pages/
│ ├── Chat.tsx # Direct messaging
│ ├── Wall.tsx # Your posts
│ ├── Feed.tsx # Posts from contacts
│ ├── Network.tsx # Peer discovery & contacts
│ └── Settings.tsx # App configuration
├── services/ # Tauri command wrappers
│ ├── identity.ts
│ ├── network.ts
│ ├── contacts.ts
│ ├── permissions.ts
│ ├── messaging.ts
│ ├── posts.ts
│ ├── feed.ts
│ └── calling.ts
├── stores/ # Zustand state management
│ ├── identity.ts
│ └── network.ts
├── types/ # TypeScript interfaces
└── styles/
└── design-system.css # CSS custom properties
src-tauri/src/
├── commands/ # Tauri command handlers
│ ├── identity.rs
│ ├── network.rs
│ ├── contacts.rs
│ ├── permissions.rs
│ ├── messaging.rs
│ ├── posts.rs
│ ├── feed.rs
│ └── calling.rs
├── services/ # Business logic
│ ├── identity_service.rs # Key management
│ ├── crypto_service.rs # Encryption/signing
│ ├── contacts_service.rs # Contact management
│ ├── permissions_service.rs # Capability grants
│ ├── messaging_service.rs # Direct messages
│ ├── posts_service.rs # Wall posts
│ ├── feed_service.rs # Feed aggregation
│ ├── content_sync_service.rs # P2P sync
│ └── calling_service.rs # Voice calls
├── db/
│ ├── mod.rs # Database initialization
│ ├── migrations/ # SQL migrations
│ └── repositories/ # Data access layer
├── models/ # Data structures
└── p2p/
├── network.rs # libp2p swarm
└── protocols/ # Request-response protocols
local_identity- Your encrypted keypairs and profilecontacts- Peer information and trust levelspermission_events- Grant/revoke events (event sourced)permissions_current- Materialized permission statemessage_events- Message lifecycle eventsmessages- Materialized messages for UIpost_events- Post lifecycle eventsposts- Materialized postspost_media- Media metadata (files stored on disk)call_history- Voice call recordssync_state- Per-peer sync progresssync_queue- Offline message queuelamport_clock- Logical clock for ordering
| Purpose | Algorithm | Notes |
|---|---|---|
| Identity signing | Ed25519 | All messages signed |
| Key agreement | X25519 | Derived from Ed25519 |
| Conversation encryption | AES-256-GCM | HKDF-derived keys |
| Key encryption | Argon2id + AES-GCM | Passphrase-based |
| Content hashing | SHA-256 | Media content-addressing |
Permissions are signed, portable capability grants:
struct PermissionGrant {
grant_id: Uuid,
issuer_peer_id: PeerId, // Who grants
subject_peer_id: PeerId, // Who receives
capability: Capability, // Chat, WallRead, Call
issued_at: u64,
expires_at: Option<u64>,
signature: Vec<u8>, // Ed25519 signature
}- MITM attacks (Noise protocol transport + E2E encryption)
- Message spoofing (all content signed with Ed25519)
- Replay attacks (nonce tracking, lamport clocks, message IDs)
- Unauthorized access (permission grants verified on every request)
- No forward secrecy (no double-ratchet yet - compromise exposes history)
- No HSM/secure enclave integration
- Connection patterns visible (metadata leakage)
- Calls use no hard-coded third-party STUN/TURN service by default; strict NAT pairs require operator-configured TURN, and group calls are capped at 4 total participants by ADR-0001
IdentityRequest/IdentityResponse- Exchange peer info
PermissionRequest- Request capability from peerPermissionGrant- Grant capability to peerPermissionRevoke- Revoke previously granted capability
DirectMessage- Encrypted message with signatureMessageAck- Delivery/read receipt
ContentManifestRequest/Response- List available postsContentFetchRequest- Request specific postMediaChunkRequest/Response- Transfer media files
One-to-one voice signaling and WebRTC call runtime paths are implemented, but release notes must be gated by the two-profile validation checklist. Group audio/video signaling must follow ADR-0001: relay-assisted small-group full mesh, maximum 4 total participants, signed roster-bound messages, and no SFU/MCU behavior without a replacement ADR. Screen sharing remains deferred until implementation and validation land.
SignalingOffer/Answer- WebRTC SDP exchangeSignalingIce- ICE candidate exchangeSignalingHangup- End call
Harbor keeps voice calls LAN/direct-capable by default and does not bundle private TURN credentials or depend on an undeclared third-party TURN service.
- Default runtime:
iceServers: [],iceTransportPolicy: "all". Browser host candidates remain enabled, so LAN/direct calls are not blocked when no TURN server is configured. - Operators/users can add
stun:,stuns:,turn:, andturns:entries in Settings → Calls. TURN/TURNS entries require username and credential fields; credentials embedded in URLs are rejected. - TURN credential persistence is explicit. The default is This session only, which is usable for the current runtime but redacts the credential from persisted settings. Save on this device stores the credential locally for operator-managed deployments.
- libp2p relay connectivity and WebRTC media relay are separate. Harbor/libp2p relays can carry call signaling, but audio media relay requires TURN/TURNS.
- If ICE fails without usable TURN, Harbor reports strict-NAT guidance. If relay-only media is requested without TURN, Harbor reports that TURN is required rather than implying libp2p relay can carry media.
- Demo and operator setup, observability, recovery, and credential rotation are documented in
docs/demo-operations.md.
Manual validation checklist for call networking changes:
- Start two local Harbor profiles on the same LAN with no ICE servers configured and confirm a voice call still reaches ICE gathering/connection through host candidates.
- Add an operator STUN or TURN test entry in Settings → Calls and confirm the generated
RTCPeerConnectionconfiguration contains the configured ICE server. - Force a controlled failure with
iceTransportPolicy: "relay"and no TURN entry; confirm the user-facing error mentions WebRTC TURN media relay and distinguishes it from libp2p relay signaling.
# Rust/Tauri release gate
.dev/bin/dev ci --language rust
# Frontend TypeScript release gate
.dev/bin/dev ci --language typescript
# Relay release gate
cargo fmt --manifest-path relay-server/Cargo.toml -- --check
cargo check --manifest-path relay-server/Cargo.toml
cargo clippy --manifest-path relay-server/Cargo.toml -- -D warnings
cargo test --manifest-path relay-server/Cargo.toml
# Interactive release evidence (desktop/WebView required)
# See docs/release-gates-calls-wall-sync.md,
# docs/voice-call-e2e-validation.md, docs/video-group-call-validation.md,
# and docs/wall-sync-multi-profile-validation.md.The codebase follows these patterns:
- Event Sourcing: All state changes are events with lamport clocks
- CQRS: Events stored separately from materialized views
- Repository Pattern: Data access abstracted behind repositories
- Service Layer: Business logic in services, commands are thin wrappers
- Identity system with encrypted key storage
- P2P networking with libp2p
- Contact management
- Permission grants/revokes
- Direct messaging (encrypted)
- Wall/blog posts with media
- Feed aggregation
- Voice calling implementation paths (signaling/runtime/UI) with automated coverage; two-profile release evidence remains a required gate
- Wall/feed sync implementation paths with automated coverage; three-profile release evidence remains a required gate
- Modern, polished UI
- Double-ratchet for forward secrecy
- Screen sharing within the ADR-0001 small-group mesh contract
- Larger group rooms beyond the 4-participant ADR-0001 cap
- Group chats
- Mobile app (iOS/Android via Tauri)
- TURN/relay demo operations and credential rotation guide for strict-NAT support
- Profile photo uploads
- Read receipts
- Typing indicators
Contributions are welcome! Please open an issue or PR.
MIT License - see LICENSE