A decentralized peer-to-peer secret sharing system with granular access control, built on libp2p and RocksDB. Share secrets securely across a distributed network without any central authority.
- Features
- Architecture
- Installation
- Quick Start
- Complete Usage Guide
- Command Reference
- Security Model
- Troubleshooting
- Building from Source
- Future Roadmap
- Contributing
- License
- Fully Decentralized: No central server or authority - peers communicate directly
- Access Control: Owner-based permissions with explicit grant/revoke capabilities
- Encrypted Transport: All network traffic secured via libp2p Noise protocol
- Persistent Storage: Local RocksDB database per peer with metadata tracking
- Peer Discovery: Automatic peer finding via Kademlia DHT
- IPC Communication: CLI communicates with daemon via Unix sockets
- Cross-Platform: Supports Linux and macOS (Intel & Apple Silicon)
┌─────────────────────────────────────────────────────────────────┐
│ P2P Secrets System │
│ │
│ ┌──────────┐ │
│ │ CLI │ User commands (store, get, share, request, etc.) │
│ └────┬─────┘ │
│ │ IPC (Unix Socket) │
│ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ DAEMON PROCESS │ │
│ │ │ │
│ │ ┌─────────────┐ ┌──────────────┐ │ │
│ │ │ IPC Server │◄─────►│ P2P Swarm │ │ │
│ │ │ │ mpsc │ │ │ │
│ │ │ - Handle │channel│ - Network │◄───┼──► Peer A │
│ │ │ CLI reqs │ │ events │ │ │
│ │ │ - Manage │ │ - Request/ │◄───┼──► Peer B │
│ │ │ requests │ │ Response │ │ │
│ │ └──────┬──────┘ └──────┬───────┘ │ │
│ │ │ │ │ │
│ │ └──────────┬──────────┘ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ Protocol Layer │ │ │
│ │ │ - Access Control │ │ │
│ │ │ - Secret Management │ │ │
│ │ │ - Request Handler │ │ │
│ │ └──────────┬───────────┘ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ Storage Layer │ │ │
│ │ │ (RocksDB) │ │ │
│ │ │ - Secrets │ │ │
│ │ │ - ACLs │ │ │
│ │ │ - Metadata │ │ │
│ │ └──────────────────────┘ │ │
│ └───────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
The command-line interface is a thin client that:
- Parses user commands
- Connects to the daemon via Unix socket (
.sockfile in db directory) - Sends
IpcRequestmessages and receivesIpcResponsemessages - Formats and displays results to the user
The daemon is a long-running background process that manages two concurrent async tasks:
IPC Server:
- Listens on Unix socket for CLI connections
- Deserializes incoming requests
- For local operations (store, get, list, delete): directly calls protocol layer
- For network operations (request): sends commands to P2P swarm via mpsc channel
- Serializes and returns responses to CLI
P2P Swarm:
- Manages libp2p network stack with these behaviors:
- Ping: Keep-alive and connectivity testing
- Identify: Peer discovery and address exchange
- Kademlia DHT: Distributed peer routing and discovery
- Request-Response: Custom protocol for secret exchange
- Listens for incoming network requests from remote peers
- Processes outgoing requests triggered by CLI
requestcommand - Handles peer connections and disconnections
Communication Flow:
CLI → Unix Socket → IPC Server → mpsc channel → P2P Swarm → Network
↓ ↓
Protocol Layer Protocol Layer
↓ ↓
Local Storage Remote Peer Check
Implements the core business logic:
Access Control:
- Owner-based permission model
- Each secret has one owner (the creator)
- Owner can grant access to specific peers via their PeerID
- Owner can revoke access at any time
- Permissions persist in RocksDB alongside secrets
Secret Management:
- Store secrets with automatic ownership assignment
- Retrieve secrets with permission validation
- Delete secrets (owner-only operation)
- Handle network requests with ACL enforcement
Request Handler: When a peer requests a secret, the handler:
- Receives
SecretRequest::Get { key, requester } - Checks if requester has permission via ACL
- Returns
SecretResponse::GetResponsewith data or access denied error
Key-value pairs stored per peer:
secret:{key} → Vec<u8> (encrypted secret data)
owner:{key} → PeerId (owner's peer ID)
acl:{key} → Vec<PeerId> (list of authorized peers)
metadata:{key} → Metadata (creation time, etc.)
peer_id → String (this peer's ID)
Each peer has its own isolated database directory, ensuring complete data separation.
Transport Stack:
Application (Secret Protocol)
↓
Request-Response Protocol
↓
Multiplexing (Yamux)
↓
Encryption (Noise)
↓
TCP Transport
Protocols Used:
- Noise Protocol: Provides transport encryption and peer authentication
- Yamux: Multiplexes multiple streams over a single connection
- Kademlia DHT: Enables peer discovery without central registry
- Custom
/secrets/1.0.0: Request-response protocol for secret exchange
Peer Discovery:
- Bootstrap peer provided on startup
- Identify protocol exchanges peer addresses
- Kademlia DHT builds routing table
- Periodic bootstrap maintains network connectivity
User: p2p-secrets store "api_key" "sk-12345" --db-path "./peer_a_db"
↓
CLI creates IpcRequest::Store { key: "api_key", value: "sk-12345" }
↓
Sends to daemon via Unix socket
↓
IPC Server receives and calls protocol.store_secret()
↓
Protocol Layer:
- storage.store_secret("api_key", "sk-12345")
- access_control.set_owner("api_key", peer_a_id)
- Persist ACL to RocksDB
↓
Returns IpcResponse::Success
↓
CLI displays: ✅ Secret 'api_key' stored successfully
User: p2p-secrets request "api_key" "12D3Koo...PeerA" --db-path "./peer_b_db"
↓
CLI: IpcRequest::Request { key: "api_key", peer_id: "12D3Koo...PeerA" }
↓
IPC Server (Peer B):
- Creates oneshot channel for response
- Sends SwarmCommand::SendRequest via mpsc to P2P Swarm
- Waits on oneshot receiver
↓
P2P Swarm (Peer B):
- Receives SwarmCommand from channel
- Creates SecretRequest::Get { key: "api_key", requester: peer_b_id }
- Sends network request to Peer A via libp2p
- Stores oneshot sender in pending_requests HashMap
↓
[Network Transit]
↓
P2P Swarm (Peer A):
- Receives network request
- Calls protocol.handle_request(request, peer_b_id)
↓
Protocol Layer (Peer A):
- Checks access_control.can_access("api_key", peer_b_id)
- If authorized: storage.get_secret("api_key")
- Returns SecretResponse::GetResponse { data: Some("sk-12345") }
↓
P2P Swarm (Peer A):
- Sends response back over network to Peer B
↓
[Network Transit]
↓
P2P Swarm (Peer B):
- Receives network response
- Looks up oneshot sender by request_id
- Sends response through oneshot channel
↓
IPC Server (Peer B):
- Receives response from oneshot channel
- Returns IpcResponse::Success { data: "sk-12345" }
↓
CLI displays: ✅ Secret 'api_key': sk-12345
Linux (x86_64):
# Download binary
curl -L https://github.com/bahbah94/p2p-secrets/releases/latest/download/p2p-secrets-linux-x64 -o p2p-secrets
# Make executable
chmod +x p2p-secrets
# Move to PATH
sudo mv p2p-secrets /usr/local/bin/macOS (Intel):
curl -L https://github.com/bahbah94/p2p-secrets/releases/latest/download/p2p-secrets-macos-x64 -o p2p-secrets
chmod +x p2p-secrets
sudo mv p2p-secrets /usr/local/bin/macOS (Apple Silicon):
curl -L https://github.com/bahbah94/p2p-secrets/releases/latest/download/p2p-secrets-macos-arm64 -o p2p-secrets
chmod +x p2p-secrets
sudo mv p2p-secrets /usr/local/bin/Verify Installation:
p2p-secrets --helpPrerequisites:
- Rust 1.70 or later
- Cargo
# Clone repository
git clone https://github.com/bahbah94/p2p-secrets.git
cd p2p-secrets
# Build release binary
cargo build --release
# Binary location
./target/release/p2p-secrets
# Optionally install
cargo install --path .p2p-secrets daemon --db-path "./peer_a_db"Output will show:
Local Peer ID: 12D3KooWLhtLikHJ1zkunuvPkiGMPP5oBw5K9vvNWiWbGeHFUwsT
Listening on /ip4/0.0.0.0/tcp/4001
Bootstrap address: /ip4/127.0.0.1/tcp/4001/p2p/12D3KooWLhtLikHJ1zkunuvPkiGMPP5oBw5K9vvNWiWbGeHFUwsT
Copy the bootstrap address for the next step.
p2p-secrets daemon --db-path "./peer_b_db" \
--bootstrap "/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWLhtLikHJ1zkunuvPkiGMPP5oBw5K9vvNWiWbGeHFUwsT"You should see "Connection established with peer" messages in both terminals.
# Store a secret in Peer A
p2p-secrets store "database_password" "super_secret_123" --db-path "./peer_a_db"
# Get Peer B's ID
p2p-secrets status --db-path "./peer_b_db"
# Note the Peer ID shown
# Share the secret with Peer B
p2p-secrets share "database_password" "12D3KooW...PeerBID..." --db-path "./peer_a_db"# Peer B requests the shared secret
p2p-secrets request "database_password" "12D3KooW...PeerAID..." --db-path "./peer_b_db"
# Output: ✅ Secret 'database_password': super_secret_123# Store a private secret (don't share)
p2p-secrets store "admin_token" "private123" --db-path "./peer_a_db"
# Try to request it from Peer B (should fail)
p2p-secrets request "admin_token" "12D3KooW...PeerAID..." --db-path "./peer_b_db"
# Output: ❌ Error: Not found or access deniedThe daemon occupies a terminal. Use these methods to run in background:
# Start screen session
screen -S peer_a
# Run daemon
p2p-secrets daemon --db-path "./peer_a_db"
# Detach: Press Ctrl+A, then D
# List sessions
screen -ls
# Reattach
screen -r peer_a
# Stop daemon: Reattach and press Ctrl+C# Create session
tmux new -s peer_a
# Run daemon
p2p-secrets daemon --db-path "./peer_a_db"
# Detach: Press Ctrl+B, then D
# Reattach
tmux attach -t peer_a
# List sessions
tmux ls# Run in background
nohup p2p-secrets daemon --db-path "./peer_a_db" > peer_a.log 2>&1 &
# Save PID
echo $! > peer_a.pid
# View logs
tail -f peer_a.log
# Stop daemon
kill $(cat peer_a.pid)
rm peer_a.pid# Terminal 1: Peer A (default port 4001)
p2p-secrets daemon --db-path "./peer_a_db"
# Terminal 2: Peer B (custom port to avoid conflict)
p2p-secrets daemon --db-path "./peer_b_db" --port 4002 \
--bootstrap "/ip4/127.0.0.1/tcp/4001/p2p/..."
# Terminal 3: Peer C
p2p-secrets daemon --db-path "./peer_c_db" --port 4003 \
--bootstrap "/ip4/127.0.0.1/tcp/4001/p2p/..."Machine A (Bootstrap Node - Public IP: 203.0.113.10):
p2p-secrets daemon --db-path "./peer_db"
# Note the Peer IDMachine B (Remote Peer):
p2p-secrets daemon --db-path "./peer_db" \
--bootstrap "/ip4/203.0.113.10/tcp/4001/p2p/12D3Koo..."Firewall Configuration: Ensure TCP port 4001 is open on the bootstrap node.
Start the P2P daemon process.
p2p-secrets daemon [OPTIONS]Options:
--db-path <PATH>- Database directory path (default:./db)--bootstrap <ADDR>- Bootstrap peer multiaddress to connect to--port <PORT>- TCP port to listen on (default:4001)
Examples:
# Start standalone peer
p2p-secrets daemon --db-path "./peer_a_db"
# Connect to bootstrap peer
p2p-secrets daemon --db-path "./peer_b_db" \
--bootstrap "/ip4/127.0.0.1/tcp/4001/p2p/12D3Koo..."
# Custom port
p2p-secrets daemon --db-path "./peer_db" --port 5001Display daemon and peer information.
p2p-secrets status --db-path <PATH>Output:
- Peer ID
- Number of connected peers
- List of connected peer IDs
- Secret count
Example:
p2p-secrets status --db-path "./peer_a_db"Test daemon connectivity.
p2p-secrets ping --db-path <PATH>Returns "Pong" if daemon is responsive.
Store a secret locally.
p2p-secrets store <KEY> <VALUE> --db-path <PATH>Arguments:
<KEY>- Unique identifier for the secret<VALUE>- Secret value to store
Example:
p2p-secrets store "api_key" "sk-1234567890abcdef" --db-path "./peer_a_db"Retrieve a locally stored secret.
p2p-secrets get <KEY> --db-path <PATH>Example:
p2p-secrets get "api_key" --db-path "./peer_a_db"
# Output: ✅ Secret 'api_key': sk-1234567890abcdefList all locally stored secrets.
p2p-secrets list --db-path <PATH>Example:
p2p-secrets list --db-path "./peer_a_db"
# Output:
# Stored secrets:
# api_key
# database_password
# admin_tokenDelete a secret (owner only).
p2p-secrets delete <KEY> --db-path <PATH>Example:
p2p-secrets delete "old_api_key" --db-path "./peer_a_db"Grant access to a secret for another peer.
p2p-secrets share <KEY> <PEER_ID> --db-path <PATH>Arguments:
<KEY>- Secret identifier<PEER_ID>- Target peer's ID (12D3Koo... format)
Example:
# Get target peer's ID first
p2p-secrets status --db-path "./peer_b_db"
# Share the secret
p2p-secrets share "api_key" "12D3KooWDDvH2KrqDZhZHZLcPSLkANdQxtW5gXgSCzw3MRp4rRWZ" \
--db-path "./peer_a_db"Revoke access to a secret from a peer.
p2p-secrets revoke <KEY> <PEER_ID> --db-path <PATH>Example:
p2p-secrets revoke "api_key" "12D3KooW..." --db-path "./peer_a_db"Request a secret from another peer over the network.
p2p-secrets request <KEY> <PEER_ID> --db-path <PATH>Arguments:
<KEY>- Secret identifier<PEER_ID>- Peer who owns/shares the secret
Important: The secret must have been shared with your peer ID first.
Example:
# Peer B requests from Peer A
p2p-secrets request "api_key" "12D3KooWLhtLikHJ..." --db-path "./peer_b_db"How it works:
- Your daemon sends a network request to the target peer
- Target peer's daemon checks if you have permission
- If authorized, secret is sent back over encrypted channel
- Secret is displayed but NOT stored locally
- Each access requires a new network request (fetch-on-demand model)
Remove database directory and all stored data.
p2p-secrets cleanup --db-path <PATH> [--force]Options:
--force- Stop daemon if running before cleanup
Example:
# Stop daemon first manually
p2p-secrets cleanup --db-path "./peer_a_db"
# Or force cleanup
p2p-secrets cleanup --db-path "./peer_a_db" --forceEncryption: All network communication is encrypted using the Noise protocol framework:
- XX handshake pattern for mutual authentication
- ChaCha20-Poly1305 for symmetric encryption
- Curve25519 for key exchange
Peer Authentication: Each peer has a unique cryptographic identity (PeerId derived from public key). Man-in-the-middle attacks are prevented by the Noise handshake.
Owner-Based Permissions:
- Creator of a secret is automatically the owner
- Only the owner can grant/revoke access
- Only the owner can delete the secret
- Permissions persist across daemon restarts
Permission Checking:
Request for secret "api_key" from Peer B
↓
Check: Is Peer B the owner?
Yes → Grant access
No → Check ACL
↓
Is Peer B in allowed list?
Yes → Grant access
No → Access denied
Secrets are stored unencrypted on disk:
- RocksDB stores secrets in plaintext
- Relies on filesystem permissions for protection
- If database directory is compromised, secrets are readable
Mitigation:
- Set proper file permissions:
chmod 700 peer_db - Store database on encrypted filesystem
- Run daemon as non-privileged user
No secret replication:
- Each secret exists on only one peer's storage
- If that peer goes offline, secret is unavailable
- Single point of failure for each secret
No secret versioning:
- Updating a secret overwrites the previous value
- No history or rollback capability
Problem: Daemon won't start
# Check if port is in use
lsof -i :4001
# If occupied, use different port
p2p-secrets daemon --db-path "./peer_db" --port 5001
# Check database isn't locked
rm -rf ./peer_db/LOCKProblem: Cannot connect to daemon (IPC errors)
# Verify daemon is running
ps aux | grep p2p-secrets
# Check socket file exists
ls -la ./peer_db/*.sock
# Restart daemon
# (Ctrl+C in daemon terminal or kill process)
p2p-secrets daemon --db-path "./peer_db"Problem: Peers cannot connect
# Verify both daemons are running
# Check network connectivity
ping <peer_ip>
# Verify firewall allows TCP 4001
sudo ufw allow 4001/tcp # Ubuntu/Debian
sudo firewall-cmd --add-port=4001/tcp --permanent # CentOS/RHEL
# Check bootstrap address is correct (copy full multiaddress)Problem: Connection established but request fails
# Verify secret was shared
p2p-secrets status --db-path "./peer_a_db"
# Check peer IDs match exactly
# Peer IDs are case-sensitive: 12D3Koo...
# Try ping to test connectivity
p2p-secrets ping --db-path "./peer_db"Problem: "Access denied" when requesting secret
# Verify share was successful
# On owner's machine:
p2p-secrets status --db-path "./owner_db"
# Check you're using correct peer ID
# Your peer ID must match what was shared to
# Re-share if needed
p2p-secrets share "secret_key" "<your_peer_id>" --db-path "./owner_db"Problem: Cannot delete secret
# Only owner can delete
# Verify you're the owner:
p2p-secrets list --db-path "./peer_db"
# Secrets you own will be listedProblem: Database corruption
# Backup if possible
cp -r ./peer_db ./peer_db_backup
# Try cleanup and restart
p2p-secrets cleanup --db-path "./peer_db" --force
# Reinitialize
p2p-secrets daemon --db-path "./peer_db"Problem: Disk space
# Check database size
du -sh ./peer_db
# List secrets
p2p-secrets list --db-path "./peer_db"
# Delete unused secrets
p2p-secrets delete "old_key" --db-path "./peer_db"# Clone repository
git clone https://github.com/bahbah94/p2p-secrets.git
cd p2p-secrets
# Install dependencies (Rust/Cargo required)
rustc --version # Should be 1.70+
# Build debug version
cargo build
# Run tests
cargo test
# Run with debug logging
RUST_LOG=debug cargo run -- daemon --db-path "./test_db"# Build optimized binary
cargo build --release
# Binary at: target/release/p2p-secrets
./target/release/p2p-secrets --help# Install cross
cargo install cross
# Build for Linux (from macOS)
cross build --release --target x86_64-unknown-linux-gnu
# Build for macOS Intel (from Linux)
cross build --release --target x86_64-apple-darwin
# Build for macOS Apple Silicon
cross build --release --target aarch64-apple-darwin
# Binaries in target/<target>/release/# Format code
cargo fmt
# Lint
cargo clippy
# Fix clippy warnings
cargo clippy --fix-
At-Rest Encryption: Encrypt secrets in RocksDB using ChaCha20-Poly1305
- Derive encryption key from user password or system keyring
- Protect secrets even if database files are compromised
-
Secret Replication: Distribute secrets across multiple peers for redundancy
- Owner specifies replica count
- Automatic failover if primary peer offline
- Consistency protocol for updates
-
Audit Logging: Track all access and modifications
- Who accessed which secrets and when
- Store logs locally and optionally share with secret owner
- Tamper-proof log chain
-
Key Rotation: Update secrets without changing access control
- Version history
- Automatic propagation to replicas
- Rollback capability
-
Web UI: Browser-based interface for secret management
- WebSocket communication with daemon
- Visual peer network graph
- Access control management UI
-
Secret Expiration: Time-based automatic deletion
- TTL (time-to-live) for secrets
- Scheduled expiration
- Expiration notifications
-
Group Permissions: Share with multiple peers at once
- Define peer groups
- Grant access to entire group
- Nested groups
-
Search and Filtering: Better secret discovery
- Tag-based organization
- Full-text search in keys
- Filter by creation date, access count, etc.
-
Mobile Apps: iOS and Android clients
- React Native or Flutter
- Push notifications for access requests
- Biometric authentication
-
Secret Templates: Pre-defined secret structures
- Database credentials template
- API key template
- SSH key template
-
Integration APIs: Easy integration with other tools
- REST API server mode
- Language-specific SDKs (Python, JavaScript, Go)
- CI/CD plugins (GitHub Actions, GitLab CI)
-
Windows Support: Native Windows binary
- Named pipes instead of Unix sockets
- Windows service support
- PowerShell integration
-
Advanced Networking: Better peer discovery and routing
- mDNS for local network discovery
- Relay nodes for NAT traversal
- DHT improvements for larger networks
-
Backup and Recovery: Export/import functionality
- Encrypted backup format
- Selective export (specific secrets)
- Restore to new peer identity
-
Monitoring and Metrics: Observability features
- Prometheus metrics export
- Grafana dashboards
- Health checks and alerts
Contributions are welcome! Here's how to get started:
Open an issue on GitHub with:
- Clear description of the problem
- Steps to reproduce
- Expected vs actual behavior
- System info (OS, Rust version)
- Relevant logs
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Make your changes
- Add tests if applicable
- Run tests:
cargo test - Format code:
cargo fmt - Lint:
cargo clippy - Commit with clear message
- Push and open PR
- Follow Rust conventions and idioms
- Write tests for new features
- Update documentation for API changes
- Keep commits atomic and well-described
- Be respectful in discussions
Be kind, respectful, and constructive. This is an open-source project for learning and collaboration.
MIT License
Copyright (c) 2025 P2P Secrets Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.