| name | wopr-p2p |
| version | 1.0.0 |
| description | Comprehensive P2P networking plugin for WOPR using Hyperswarm, identity management, topic-based discovery, and secure peer communication |
| category | network |
| tags | ["p2p","hyperswarm","networking","identity","encryption","discovery","security","a2a"] |
| triggers | ["p2p","peer","hyperswarm","discovery","invite","peer-to-peer","network","swarm","topic","identity"] |
| requires | ["wopr","wopr-security"] |
WOPR P2P Plugin
The P2P plugin provides decentralized peer-to-peer networking for WOPR using Hyperswarm DHT. It enables secure communication between WOPR nodes without central servers, using Ed25519/X25519 cryptography for identity and encryption.
Installation
wopr plugin install wopr-plugin-p2p
wopr plugin enable wopr-plugin-p2p
wopr p2p status
The plugin automatically creates a P2P identity on first use, stored at ~/.wopr/identity.json.
Core Concepts
Identity
Every WOPR node has a unique cryptographic identity:
| Key Type | Algorithm | Purpose |
|---|
| Signing Key | Ed25519 | Authenticating messages |
| Encryption Key | X25519 | Key exchange for E2E encryption |
| Ephemeral Key | X25519 | Forward secrecy per session |
Identity files are stored with 0600 permissions in ~/.wopr/.
Hyperswarm
The plugin uses Hyperswarm DHT for peer discovery and connection:
- Topics: SHA-256 hashes used for peer discovery
- DHT: Distributed hash table for finding peers
- Connections: Direct encrypted streams between peers
Trust Model
Peers are categorized by trust level (see wopr-security skill):
| Level | Description | Capabilities |
|---|
| Untrusted | Discovered via DHT | Gateway access only |
| Semi-trusted | Claimed invite | Limited sessions |
| Trusted | Explicit grant | Full access to specified sessions |
CLI Commands
Identity Management
wopr p2p status
wopr p2p rotate --reason scheduled
wopr p2p identity --export
Peer Management
wopr p2p peers
wopr p2p name <peer-id> "alice"
wopr p2p find alice
wopr p2p revoke alice
Invites and Claims
wopr p2p invite create <their-pubkey> --sessions "session1,session2"
wopr p2p invite create <their-pubkey> --sessions "*" --expire 24h
wopr p2p invite claim "wop1://..."
Access Grants
wopr p2p grant <peer-pubkey> session1 session2
wopr p2p grant <peer-pubkey> session1 --trust trusted
wopr p2p grant <peer-pubkey> session1 --capabilities "inject,inject.tools"
wopr p2p grants
wopr p2p grants --peer alice
Discovery
wopr p2p topic join myproject
wopr p2p topic leave myproject
wopr p2p topics
wopr p2p discover
wopr p2p discover --topic myproject
wopr p2p connect <peer-id>
Messaging
Two modes of P2P messaging:
wopr p2p log alice --session main --message "FYI: deployment complete"
wopr p2p inject alice --session main --message "What's the status of the build?"
wopr p2p inject alice --session main --message "Analyze this code" --timeout 300s
When to use which:
log - Notifications, status updates, fire-and-forget messages
inject - Questions, tasks, anything needing an AI response
A2A Tools
The plugin registers tools for agent-to-agent communication:
Identity Tools
| Tool | Description |
|---|
p2p_get_identity | Get or create P2P identity |
p2p_rotate_keys | Rotate identity keys |
Peer Tools
| Tool | Description |
|---|
p2p_list_peers | List all known peers |
p2p_name_peer | Assign friendly name |
p2p_revoke_peer | Revoke peer access |
Invite Tools
| Tool | Description |
|---|
p2p_create_invite | Create invite token |
p2p_claim_invite | Claim invite from peer |
Access Tools
| Tool | Description |
|---|
p2p_grant_access | Manually grant access |
p2p_list_grants | List all access grants |
Discovery Tools
| Tool | Description |
|---|
p2p_join_topic | Join discovery topic |
p2p_leave_topic | Leave discovery topic |
p2p_list_topics | List active topics |
p2p_discover_peers | List discovered peers |
p2p_connect_peer | Request connection |
p2p_get_profile | Get discovery profile |
p2p_set_profile | Update discovery profile |
Messaging Tools
| Tool | Description |
|---|
p2p_log_message | Log message to peer's session (fire-and-forget, no AI response) |
p2p_inject_message | Inject message and invoke peer's AI (get response back) |
p2p_status | Get P2P network status |
Security Scenarios
Scenario 1: Trusted Peer Setup
For peers you fully trust (e.g., your own devices):
wopr p2p status
wopr p2p status
wopr p2p invite create <nodeB-pubkey> --sessions "*"
wopr p2p invite claim "wop1://eyJ2Ijo..."
wopr p2p name <nodeB-shortId> "my-laptop"
Scenario 2: Semi-Trusted Peer (Limited Access)
For collaborators who should only access specific sessions:
wopr p2p invite create <peer-pubkey> --sessions "shared-project,docs" --expire 72h
wopr p2p grants --peer <peer-id>
Scenario 3: Untrusted Discovery (Gateway Pattern)
For unknown peers discovered via topics:
wopr p2p topic join public-agents
wopr session create public-gateway "You validate and filter external requests"
wopr security session public-gateway access "trust:untrusted"
wopr p2p grant <discovered-peer-key> public-gateway --capabilities "inject"
Scenario 4: Emergency Key Rotation
If you suspect key compromise:
wopr p2p rotate --reason compromise --notify-all
Key Rotation
Automatic Rotation
The plugin supports key rotation to limit exposure from compromised keys.
wopr p2p rotate --reason scheduled
wopr p2p rotate --reason compromise
wopr p2p rotate --reason upgrade
Grace Period
When keys are rotated, old keys remain valid for a grace period:
| Reason | Grace Period |
|---|
| scheduled | 24 hours |
| upgrade | 24 hours |
| compromise | 24 hours |
Rate Limiting
The plugin enforces per-peer rate limits to prevent abuse:
| Operation | Limit | Ban Duration |
|---|
| Injects | 10/minute, 100/hour | 1 hour |
| Claims | 5/minute, 20/hour | 1 hour |
| Invalid Messages | 3/minute, 10/hour | 2 hours |
When rate limited, peers receive a reject message with reason "rate limited".
Replay Protection
Messages include nonces and timestamps to prevent replay attacks:
{
"nonce": "a1b2c3d4e5f6...",
"ts": 1704067200000,
"sig": "MEUCIQDn..."
}
Protection Window: 5 minutes
Max Tracked Nonces: 10,000
Messages older than 5 minutes or with previously-seen nonces are rejected.
Payload Limits
To prevent memory exhaustion attacks:
| Limit | Value | Description |
|---|
MAX_PAYLOAD_SIZE | 1 MB | Maximum encrypted payload |
MAX_MESSAGE_SIZE | ~1 MB + 4 KB | Payload + protocol overhead |
Forward Secrecy
Protocol v2 uses ephemeral X25519 keypairs for each session:
Session Start:
1. Each peer generates ephemeral X25519 keypair
2. Ephemeral public keys exchanged in handshake
3. Shared secret derived via ECDH
4. Messages encrypted with ephemeral-derived key
Security Benefit:
If long-term key compromised later,
past messages remain secure
Encryption
Message Encryption (AES-256-GCM)
+------------------------+
| IV (12 bytes) |
+------------------------+
| Auth Tag (16 bytes) |
+------------------------+
| Encrypted Data |
+------------------------+
Key Derivation
Static Keys (Legacy):
SharedSecret = X25519(myPriv, theirPub)
Key = SHA256(SharedSecret)
Ephemeral Keys (Forward Secrecy):
SharedSecret = X25519(ephemeralPriv, theirEphemeralPub)
Key = SHA256(SharedSecret)
Monitoring
View Connection Status
wopr p2p status
wopr p2p peers --verbose
wopr p2p events --follow
Security Events
| Event | Description |
|---|
p2p.peer.connected | Peer connection established |
p2p.peer.rejected | Peer connection rejected |
p2p.message.rejected | Message rejected (rate limit, size, auth) |
p2p.grant.created | Access grant created |
p2p.grant.revoked | Access grant revoked |
p2p.key.rotated | Peer key rotation processed |
Revoking Access
wopr p2p revoke alice
wopr p2p revoke <peer-pubkey>
wopr p2p revoke alice --session sensitive-session
After revocation, the peer receives reject messages with reason "unauthorized".
Configuration
Plugin Configuration
In ~/.wopr/config.json:
{
"plugins": {
"data": {
"p2p": {
"discoveryTrust": "untrusted",
"autoAccept": false,
"keyRotationGraceHours": 24,
"maxPayloadSize": 1048576,
"rateLimit": {
"injects": { "maxPerMinute": 10, "maxPerHour": 100 },
"claims": { "maxPerMinute": 5, "maxPerHour": 20 }
}
}
}
}
}
Environment Variables
| Variable | Description | Default |
|---|
WOPR_P2P_AUTO_ACCEPT | Auto-accept discovered peers | false |
WOPR_P2P_DISCOVERY_TRUST | Trust level for discovery | untrusted |
WOPR_P2P_MAX_PAYLOAD | Max payload size (bytes) | 1048576 |
WOPR_P2P_KEY_GRACE_HOURS | Key rotation grace period | 24 |
Best Practices
Identity Security
- Protect identity files: Stored at
~/.wopr/identity.json with 0600 permissions
- Regular key rotation: Rotate keys quarterly or after personnel changes
- Immediate rotation on compromise: Use
--reason compromise if keys may be exposed
Access Control
- Never auto-accept discovery peers: Keep
autoAccept: false (default)
- Use specific session grants: Avoid wildcard (
*) for untrusted peers
- Implement gateway sessions: Route untrusted traffic through validation layer
- Audit grants regularly:
wopr p2p grants to review active access
Network Security
- Use topic namespacing: Include unique identifiers in topic names
- Monitor rate limit events: Watch for abuse patterns
- Set appropriate timeouts: Shorter timeouts for untrusted peers
Invite Token Security
- Short expiration times: Use
--expire 24h for sensitive access
- Single-use invites: Create new tokens for each peer
- Secure token transmission: Share tokens via encrypted channels
Troubleshooting
Peer Not Found
wopr p2p peers
wopr p2p find alice
wopr p2p find <short-id>
wopr p2p find <public-key>
Claim Failed
Common causes:
- Issuer offline - they must be running WOPR
- Token expired - check expiration
- Token for wrong peer - check 'sub' field matches your pubkey
Connection Timeout
wopr p2p inject alice --session main --message "test" --timeout 30s
wopr p2p discover --topic myproject
Rate Limited
Wait for rate limit to expire (1-2 hours) or check peer status:
wopr p2p status --peer <peer-id>
Unauthorized
wopr p2p grants --peer alice
Protocol Reference
Message Types
| Type | Purpose |
|---|
hello | Initial handshake with version negotiation |
hello-ack | Handshake acknowledgment |
inject | Send encrypted payload to session |
ack | Message accepted |
reject | Message rejected (with reason) |
claim | Claim invite token |
key-rotation | Notify of key rotation |
Protocol Versions
| Version | Features |
|---|
| 1 | Basic messaging, static key encryption |
| 2 | Forward secrecy, ephemeral keys, version negotiation |
Exit Codes
| Code | Constant | Meaning |
|---|
| 0 | EXIT_OK | Success |
| 1 | EXIT_OFFLINE | Peer offline |
| 2 | EXIT_REJECTED | Request rejected |
| 3 | EXIT_INVALID | Invalid request |
| 4 | EXIT_RATE_LIMITED | Rate limited |
| 5 | EXIT_VERSION_MISMATCH | Protocol version mismatch |
| 6 | EXIT_PEER_OFFLINE | Specific peer offline |
| 7 | EXIT_UNAUTHORIZED | Not authorized |
Data Storage
All P2P data stored in ~/.wopr/:
| File | Permissions | Content |
|---|
identity.json | 0600 | Private keys, public keys |
access.json | 0600 | Access grants |
peers.json | 0600 | Known peers |
Related Documentation