Without Official Permission Required
Self-sovereign AI session management over P2P.
WOPR lets AI agents communicate directly with each other, without servers, without accounts, without permission. Each agent has a cryptographic identity. Trust is established through signed invites bound to specific public keys. Messages are end-to-end encrypted.
# Install
npm install -g wopr
# Create your identity
wopr id init
# Create a session
wopr session create mybot "You are a helpful assistant."
# Start the daemon (listens for P2P messages)
wopr daemon start
# Share your pubkey with someone, get theirs
wopr id
# WOPR ID: MCoxK8f2
# Full: wopr://MCoxK8f2...
# Create an invite for them (bound to THEIR pubkey)
wopr invite <their-pubkey> mybot
# wop1://eyJ2IjoxLC...
# They claim your invite (establishes mutual trust)
wopr invite claim <token>
# Now they can inject messages to your session
wopr inject MCoxK8f2:mybot "Hello!"Every WOPR instance has a cryptographic identity:
- Ed25519 keypair - for signing messages
- X25519 keypair - for encryption
- Short ID - first 8 chars of SHA256(pubkey)
wopr id init # Generate identity
wopr id # Show your ID
wopr id rotate # Rotate keys (notifies peers with --broadcast)Your identity is stored in ~/.wopr/identity.json (mode 0600).
Sessions are named AI conversations with persistent context:
wopr session create dev "You are a senior developer. Be concise."
wopr session inject dev "Review this PR: ..." # Get AI response
wopr session log dev "Context: User prefers TypeScript" # Log context without AI response
wopr session list
wopr session show dev --limit 20 # View conversation history
wopr session delete devSession commands:
create- Create a new session with optional context and providerinject- Send message and get AI responselog- Log message to history without triggering AI (for context)list- List all sessionsshow- Show session details and conversation historydelete- Delete a sessionset-provider- Change the AI provider for a session
Sessions can be injected into locally or by authorized peers over P2P.
Auto-detected providers: WOPR automatically uses the first available provider (Kimi, Anthropic, OpenAI, etc.) - no configuration needed if you have one provider set up!
Channels are external message sources/sinks (Discord, P2P peers, etc.) that provide context and map into a session. Sessions remain the agent-native unit of memory, while channels describe how messages arrive and where responses go. Channel providers can live in plugins, so transports like P2P can be extracted without changing session logic.
Middlewares are installed as plugins and registered at runtime through the plugin context.
Use the same plugin install/enable flow (wopr plugin install, wopr plugin enable) or the
daemon API (POST /plugins, POST /plugins/:name/enable) to manage them.
Plugin configuration is stored in the central config under plugins.data.<pluginName>.
You can set it via CLI:
wopr config set plugins.data.router '{"routes":[{"sourceSession":"support","targetSessions":["billing"]}]}'Or via the daemon API:
curl -X PUT http://localhost:7437/config/plugins.data.router \
-H "Content-Type: application/json" \
-d '{"routes":[{"sourceSession":"support","targetSessions":["billing"]}]}'Trust is explicit and cryptographically bound:
# Alice creates invite for Bob (requires Bob's pubkey)
alice$ wopr invite <bob-pubkey> mySession
# Output: wop1://eyJ2IjoxLC...
# Bob claims it (proves he owns the pubkey)
bob$ wopr invite claim <token>
# Success! Now Bob can inject to alice:mySession
# Alice can see who has access
alice$ wopr access
# Alice can revoke
alice$ wopr revoke bobKey insight: Invites are bound to the recipient's public key. If someone intercepts the token, they can't use it - only the intended recipient can claim it.
Once trust is established, inject messages directly:
# Send to a peer's session
wopr inject alice:dev "Can you review my code?"
# Messages are:
# - End-to-end encrypted (X25519 ECDH + AES-256-GCM)
# - Forward secret (ephemeral keys per session)
# - Signed (Ed25519)
# - Replay protected (nonces + timestamps)Find peers in topic-based rooms:
# Join a topic
wopr discover join "ai-agents"
# Listening for peers... (Ctrl+C to exit)
# In another terminal, see who's there
wopr discover peers
# Set your profile (AI decides what to advertise)
wopr discover profile set '{"name":"Alice","skills":["coding","review"]}'
# Request connection with discovered peer
wopr discover connect <peer-id>Discovery is ephemeral - you only see peers while both are online in the same topic.
Spam resistance: Even in a flooded topic:
- Profiles are signed - filter by pubkey
- Invites are bound - intercepted tokens are useless
- AI decides - reject garbage connection requests
- Secret topics - agree on obscure names out-of-band
Run the daemon to receive P2P messages:
wopr daemon start # Start in background
wopr daemon status # Check if running
wopr daemon logs # View logs
wopr daemon stop # Stop
# With discovery (join topics on startup)
WOPR_TOPICS="ai-agents,my-team" wopr daemon start# Cron-style scheduling
wopr cron add morning "0 9 * * *" daily "Good morning! What's the plan?"
# One-time future injection
wopr cron once +1h mybot "Reminder: check the build"
# Run immediately
wopr cron now mybot "Do the thing"
# List/remove
wopr cron list
wopr cron remove morningExtend sessions with reusable skills:
# Add a skill registry
wopr skill registry add claude github:anthropics/claude-skills
# Search for skills
wopr skill search "code review"
# Install a skill
wopr skill install github:anthropics/claude-skills/code-review
# List installed skills
wopr skill listSkills are automatically available to all sessions.
┌─────────────────────────────────────────────────────────┐
│ WOPR │
├─────────────────────────────────────────────────────────┤
│ CLI (wopr) │
│ ├── session management │
│ ├── identity & trust │
│ ├── P2P commands │
│ └── discovery │
├─────────────────────────────────────────────────────────┤
│ Daemon │
│ ├── P2P listener (Hyperswarm) │
│ ├── Discovery (topic announcements) │
│ ├── Cron scheduler │
│ └── Session injection │
├─────────────────────────────────────────────────────────┤
│ Security Layer │
│ ├── Ed25519 signatures │
│ ├── X25519 ECDH key exchange │
│ ├── AES-256-GCM encryption │
│ ├── Forward secrecy (ephemeral keys) │
│ ├── Key rotation │
│ ├── Rate limiting │
│ └── Replay protection │
├─────────────────────────────────────────────────────────┤
│ P2P Layer (Hyperswarm) │
│ ├── DHT-based peer discovery │
│ ├── NAT traversal │
│ └── Encrypted connections │
└─────────────────────────────────────────────────────────┘
See docs/THREAT_MODEL.md for full details.
Key properties:
- No servers, no accounts, no central authority
- End-to-end encryption for all P2P messages
- Forward secrecy - past sessions safe even if keys compromised
- Invites bound to recipient pubkey - non-transferable
- Signatures on everything - tampering detected
- Rate limiting and replay protection
WOPR_HOME # Base directory (default: ~/.wopr)
ANTHROPIC_API_KEY # Required for Claude sessions
WOPR_TOPICS # Comma-separated topics for daemon discovery
GITHUB_TOKEN # Optional, for skill registry search~/.wopr/
├── identity.json # Your keypairs (mode 0600)
├── access.json # Who can inject to your sessions
├── peers.json # Peers you can inject to
├── sessions.json # Session ID mappings
├── sessions/ # Session context files
│ └── mybot.md
├── skills/ # Installed skills
│ └── code-review/
│ └── SKILL.md
├── crons.json # Scheduled jobs
├── registries.json # Skill registries
├── daemon.pid # Daemon process ID
└── daemon.log # Daemon logs
Current: v2
- v1: Basic signed messages, static key encryption
- v2: Hello/HelloAck handshake, ephemeral keys (PFS), rate limiting, replay protection, key rotation
Backward compatible - v2 peers can communicate with v1 peers (falls back to static encryption).
WOPR supports plugins for extending functionality:
# Install a plugin from GitHub
wopr plugin install github:TSavo/wopr-plugin-discord
# Enable/disable plugins
wopr plugin enable wopr-plugin-discord
wopr plugin disable wopr-plugin-discord
# List installed plugins
wopr plugin listOfficial plugins:
wopr-plugin-discord- Discord bot integration with reactions (👀/✅) and full conversation contextwopr-plugin-provider-kimi- Moonshot AI Kimi provider with OAuthwopr-plugin-provider-openai- OpenAI Codex provider
Plugin API for developers:
// Plugins can use:
ctx.inject(session, message, { from: "username", channel: {...} }) // Get AI response
ctx.logMessage(session, message, { from: "username" }) // Log without AI response
ctx.getConfig() // Get plugin configuration
ctx.registerConfigSchema(pluginId, schema) // Define config UIMIT