Warning: This was made purely out of curiosity if it was possible as a test. It has not been audited. Use at your own risk
There is no running SeedShell relay. If you want to use the socket relay server, you will have to run it yourself and customize the relay address both in running the agent and on the browser side. The WebRTC connection method is currently the easiest.
A secure browser-based terminal with end-to-end encryption. Supports two connection modes:
- P2P WebRTC: Direct peer-to-peer via Nostr signaling (no relay server needed)
- WebSocket Relay: Connect through a relay server (self-hosted or managed)
┌─────────────┐ ┌─────────────┐
│ Browser │◄══════════════════════════════════►│ Agent │
│ (Next.js) │ Direct WebRTC P2P │ (Go) │
│ │ │ │
│ • xterm.js │ ┌─────────────────────────┐ │ • PTY shell │
│ • NaCl box │ │ Nostr Relays │ │ • NaCl box │
│ • BIP-39 │ │ (signaling only) │ │ • BIP-39 │
│ • WebRTC │ │ • relay.damus.io │ │ • Pion │
└──────┬──────┘ │ • nos.lol │ └──────┬──────┘
│ │ • relay.nostr.band │ │
│ └─────────────────────────┘ │
│ ▲ ▲ │
│ 1. SDP Offer │ │ 2. SDP Answer │
└──────────────────────┘ └─────────────────────────┘
(Signaling disconnects after P2P established)
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Browser │◄═══════►│ Relay │◄═══════►│ Agent │
│ (Next.js) │ WSS │ Server │ WSS │ (Go) │
│ │ │ │ │ │
│ • xterm.js │ │ • Room mgmt │ │ • PTY shell │
│ • NaCl box │ │ • Forward │ │ • NaCl box │
│ • BIP-39 │ │ │ │ • BIP-39 │
└─────────────┘ └─────────────┘ └─────────────┘
- Agent Startup: Agent connects to public Nostr relays and subscribes to its room (agent ID)
- Browser Connection: Browser connects to same Nostr relays, sends WebRTC offer
- Signaling: SDP and ICE candidates exchanged via Nostr events
- P2P Established: Direct WebRTC DataChannel connection established
- Nostr Disconnected: Once P2P is up, Nostr signaling is no longer needed
- Authentication: Browser proves it has the seed phrase via encrypted challenge
- Terminal Session: All terminal I/O flows directly between browser and agent, encrypted with NaCl
- Agent Startup: Agent connects to relay server and joins a room (agent ID)
- Browser Connection: Browser connects to same relay and joins the same room
- Authentication: Browser proves it has the seed phrase via encrypted challenge
- Terminal Session: All terminal I/O flows through relay, encrypted with NaCl (relay cannot read content)
SeedShell supports automatic agent discovery, so you don't need to manually enter the Agent ID. Discovery works in both connection modes:
- Shared Secret: Both the agent and browser derive a "discovery room ID" and "announcement encryption key" from the seed phrase
- Agent Heartbeat: The agent periodically broadcasts encrypted announcements containing its Agent ID and connection mode
- Browser Listening: When you enter your seed phrase and click "Discover Online Agents", the browser listens for these announcements
- Decryption: Only clients with the same seed phrase can decrypt and see the agent announcements
- One-Click Connect: Discovered agents appear in a list - click to connect instantly
| Mode | Discovery Channel | How It Works |
|---|---|---|
| WebRTC | Nostr relays | Agent publishes encrypted heartbeats to a derived Nostr "d" tag (room ID) |
| Relay | WebSocket relay server | Agent registers with relay; relay broadcasts presence to subscribers |
Privacy Note: Discovery announcements are encrypted with a key derived from your seed phrase. Only someone with the same seed phrase can discover your agents.
- Seed Phrase Authentication: A 12-word BIP-39 mnemonic generates deterministic Ed25519/Curve25519 keypairs
- Zero-Knowledge: Relay servers and Nostr relays only see encrypted data - they cannot read any terminal content
- End-to-End Encryption: All data is encrypted with NaCl before leaving the browser/agent
- No Key Storage: Keys exist only in browser memory and are wiped when the tab closes
- Mutual Authentication: Both browser and agent must derive the same key from the seed phrase
- Discovery Privacy: Agent discovery uses seed-derived encryption - only authorized clients can see your agents
# Install Node.js dependencies
npm install
# Install Go dependencies
cd agent && go mod download && cd ..cd agent
go build -o agent .Running without --seed will automatically generate a new 12-word seed phrase and start the agent immediately:
./agentOutput:
========================================
NEW SEED PHRASE GENERATED - SAVE THIS!
========================================
apple banana cherry date elder fig grape honey iris jade kite lemon
Use this seed phrase in the browser to connect.
To reuse this seed, run with: -seed "<phrase>"
========================================
SeedShell Agent Online (P2P Mode)
Agent ID: swift-tiger-042
Important: Save this seed phrase! You'll need it to connect from the browser and to restart the agent with the same identity.
To maintain the same cryptographic identity across restarts, provide your saved seed phrase:
./agent -seed "apple banana cherry date elder fig grape honey iris jade kite lemon"By default, a random name like swift-tiger-042 is generated. Use --name to specify your own:
./agent -name "home-server" -seed "your seed phrase here"
./agent -name "work-laptop" -seed "your seed phrase here"Since authentication is based on the seed phrase (not the agent name), you can use any name you like - even duplicate names across different machines.
npm run dev:client
# Opens at http://localhost:3000- Open http://localhost:3000 in your browser
- Select connection mode (P2P WebRTC or Relay)
- Enter the same seed phrase used on the agent
- Either:
- Click "Discover Online Agents" to auto-discover and select your agent, or
- Manually enter the Agent ID (e.g.,
swift-tiger-042)
- Click Connect
| Flag | Default | Description |
|---|---|---|
-seed |
Auto-generated | BIP-39 seed phrase (generates new if omitted) |
-name |
Auto-generated | Custom agent name (e.g., "home-server") |
-mode |
webrtc |
Connection mode: webrtc or relay |
-relay |
wss://relay.seedshell.app |
WebSocket relay URL (relay mode) |
-nostr-relays |
Public Nostr relays | Comma-separated Nostr relay URLs |
-id |
(deprecated) | Use -name instead |
# First run - generate seed, auto-generate name, WebRTC mode (default)
./agent
# Reuse seed with custom name
./agent -name "my-server" -seed "your twelve word seed phrase here"
# WebRTC mode with custom Nostr relays
./agent -mode webrtc -seed "your seed phrase" \
-nostr-relays "wss://relay.damus.io,wss://nos.lol"
# Relay mode with custom relay server
./agent -mode relay -name "office-pc" -seed "your seed phrase" \
-relay "wss://your-relay.example.com"wss://relay.damus.iowss://nos.lolwss://relay.nostr.band
If you want to self-host the relay server:
cd relay
npm install
npm start
# Listens on port 8080 by default (set PORT env var to change)For production, deploy behind a reverse proxy with TLS (nginx, Caddy, etc.).
npm run dev:client
# Starts the Next.js client with hot reloadnpm run dev:relay
# Starts the relay server on port 8080seedshell/
├── client/ # Next.js frontend
│ ├── src/
│ │ ├── app/ # Next.js app router
│ │ ├── components/ # React components
│ │ └── lib/ # Crypto, connection, & signaling
│ └── package.json
├── agent/ # Go agent binary
│ ├── main.go
│ └── go.mod
├── relay/ # WebSocket relay server
│ ├── server.js
│ └── package.json
└── package.json # Workspace root
Build for your target platform:
cd agent
# Linux
GOOS=linux GOARCH=amd64 go build -o seedshell-agent-linux .
# macOS
GOOS=darwin GOARCH=amd64 go build -o seedshell-agent-macos .
# Windows
GOOS=windows GOARCH=amd64 go build -o seedshell-agent.exe .cd client
npm run build
# Deploy the .next output to any static host (Vercel, Netlify, etc.)cd relay
# Deploy to any Node.js host (Railway, Render, DigitalOcean, etc.)
# Ensure TLS is configured (use reverse proxy or platform-provided TLS)- Seed phrase security: Treat the seed phrase like a password. Anyone with the phrase can access the machine.
- Relay trust: In relay mode, the relay server cannot read your data (it's encrypted), but it can see connection metadata.
- Nostr metadata: Nostr relays can see connection metadata (when you connect, IP addresses) but not content.
- WebRTC IP exposure: WebRTC may expose your real IP even behind VPN. Use TURN servers if this is a concern.
- Browser security: Keys are held in memory; a compromised browser could theoretically extract them.
- NAT traversal: Some restrictive NATs may prevent direct P2P connections in WebRTC mode. Use relay mode as fallback.
- Agent naming: Agent names are just human-friendly labels with no security function. Security comes entirely from the seed phrase.
| Feature | WebRTC (P2P) | Relay |
|---|---|---|
| Infrastructure | Public Nostr relays | Self-hosted or managed relay |
| Data path | Browser ↔ Agent (direct) | Browser → Relay → Agent |
| Latency | Lower (P2P) | Higher (relay hop) |
| NAT traversal | May fail on restrictive NATs | Works everywhere |
| Signaling | Only during setup | Continuous |
| Self-hosting | Nothing to host | Relay server needed |
| Auto-discovery | Via Nostr heartbeats | Via relay presence |
github.com/pion/webrtc/v3- WebRTC implementationgithub.com/nbd-wtf/go-nostr- Nostr clientgithub.com/gorilla/websocket- WebSocket client (relay mode)github.com/creack/pty- PTY managementgithub.com/tyler-smith/go-bip39- BIP-39 mnemonicsgolang.org/x/crypto- NaCl encryption
nostr-tools- Nostr clienttweetnacl- NaCl encryptionbip39- BIP-39 mnemonics@xterm/xterm- Terminal emulator
ws- WebSocket server
MIT