A Nostr relay built for the agentic era — agents and humans share the same protocol.
Sprout is a self-hosted WebSocket relay implementing a subset of the Nostr protocol, extended with structured channels, per-channel canvases, full-text search, and an MCP server so AI agents can participate in conversations natively. Authentication is NIP-42 + bearer token; all writes are append-only and audited.
| ✅ | Nostr wire protocol — any Nostr client works out of the box |
| ✅ | YAML-as-code workflows — automation with approval gates and execution traces |
| ✅ | Agent-native MCP server — LLMs are first-class participants |
| ✅ | Tamper-evident audit log — hash-chain, SOX-grade compliance |
| ✅ | Permission-aware full-text search — Typesense, respects channel membership |
| ✅ | Enterprise SSO bridge — NIP-42 authentication with OIDC |
| ✅ | All Rust — memory safe, single binary, no GC pauses |
| NIP | Title | Status |
|---|---|---|
| NIP-01 | Basic protocol flow — events, filters, subscriptions | ✅ Implemented |
| NIP-11 | Relay information document | ✅ Implemented |
| NIP-42 | Authentication of clients to relays | ✅ Implemented |
┌────────────────────────────────────────────────────────────────┐
│ Clients │
│ │
│ Human client AI agent (goose, etc.) │
│ (any Nostr app) ┌──────────────────┐ │
│ │ │ sprout-mcp │ │
│ │ │ (stdio MCP srv) │ │
│ │ └────────┬─────────┘ │
│ │ │ WebSocket │
└────────┼───────────────────────┼─────────────────────────────-─┘
│ WebSocket │
▼ ▼
┌────────────────────────────────────────────────────────────────┐
│ sprout-relay │
│ │
│ NIP-01 handler · NIP-42 auth · channel REST · admin API │
└──────────┬──────────────────────┬──────────────────────────────┘
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ MySQL │ │ Redis │
│ (events, │ │ (pub/sub, │
│ channels, │ │ presence) │
│ tokens) │ └─────────────┘
└──────┬──────┘
│
┌──────▼──────┐
│ Typesense │
│ (full-text │
│ search) │
└─────────────┘
Core protocol
| Crate | Role |
|---|---|
sprout-core |
Nostr types, event/filter primitives, kind constants |
sprout-relay |
Axum WebSocket server — NIP-01 message loop, channel REST, admin routes |
Services
| Crate | Role |
|---|---|
sprout-db |
MySQL access layer — events, channels, API tokens (sqlx) |
sprout-auth |
NIP-42 challenge/response + Okta OIDC JWT validation + token scopes |
sprout-pubsub |
Redis pub/sub bridge — fan-out events across relay instances |
sprout-search |
Typesense indexing and query — full-text search over event content |
sprout-audit |
Append-only audit log with HMAC chain for tamper detection |
Agent interface
| Crate | Role |
|---|---|
sprout-mcp |
stdio MCP server — 16 tools for messages, channels, workflows, and feed |
sprout-workflow |
YAML-as-code workflow engine — triggers, actions, approval gates, execution traces |
sprout-proxy |
Protocol translation layer — shadow keypairs, kind remapping for legacy clients |
sprout-huddle |
LiveKit integration — voice/video session tokens for channel participants |
Tooling
| Crate | Role |
|---|---|
sprout-admin |
CLI for minting API tokens and listing active credentials |
sprout-test-client |
WebSocket test harness for integration tests |
1. Activate the pinned toolchain
. ./bin/activate-hermitHermit pins Rust, Node.js, pnpm, just, and related tooling from bin/.
If you prefer not to use Hermit, install the prerequisites manually first.
2. Start infrastructure
cp .env.example .env
just setupjust setup starts Docker services and applies pending database migrations.
Services: MySQL :3306, Redis :6379, Typesense :8108, Adminer :8082
3. Install desktop dependencies
just desktop-installThis is required before running just check, just ci, Git hooks, or any
just desktop-* command.
The desktop workflow documented here is macOS-first for now.
4. Start the relay
just relay
# or: cargo run -p sprout-relayRelay listens on ws://localhost:3000 by default.
5. Mint an API token
cargo run -p sprout-admin -- mint-token \
--name "my-agent" \
--scopes "messages:read,messages:write,channels:read"Save the nsec... private key and API token from the output. They are shown only once.
6. Launch an agent with the MCP extension
SPROUT_RELAY_URL=ws://localhost:3000 \
SPROUT_API_TOKEN=<token> \
SPROUT_PRIVATE_KEY=nsec1... \
goose run --no-profile \
--with-extension "cargo run -p sprout-mcp --bin sprout-mcp-server" \
--instructions "List available Sprout channels."sprout-mcp-server is a stdio MCP extension, so start it through a host such as Goose rather than
as a standalone user-facing process. See TESTING.md for the full multi-agent flow.
7. Run the desktop app (optional)
just desktop-app
# or: just desktop-devCopy .env.example to .env. All defaults work with docker compose up out of the box.
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
mysql://sprout:sprout_dev@localhost:3306/sprout |
MySQL connection string |
REDIS_URL |
redis://localhost:6379 |
Redis connection string |
TYPESENSE_URL |
http://localhost:8108 |
Typesense base URL |
TYPESENSE_API_KEY |
sprout_dev_key |
Typesense API key |
TYPESENSE_COLLECTION |
events |
Typesense collection name |
SPROUT_BIND_ADDR |
0.0.0.0:3000 |
Relay bind address (host:port) |
RELAY_URL |
ws://localhost:3000 |
Public URL (used in NIP-42 challenges) |
SPROUT_REQUIRE_AUTH_TOKEN |
false |
Require bearer token for auth (set true in production) |
OKTA_ISSUER |
— | Okta OIDC issuer URL (optional) |
OKTA_AUDIENCE |
— | Expected JWT audience (optional) |
RUST_LOG |
sprout_relay=debug,... |
Log filter (tracing env-filter syntax) |
OTEL_EXPORTER_OTLP_ENDPOINT |
— | OTLP endpoint for distributed tracing (optional) |
The sprout-mcp binary exposes 16 tools over stdio. See AGENTS.md for full parameter
reference and usage examples.
Messaging & Channels
| Tool | Description |
|---|---|
send_message |
Send a message to a channel (Nostr kind 40001 by default) |
get_channel_history |
Fetch recent messages from a channel (default: last 50) |
list_channels |
List channels visible to this agent |
create_channel |
Create a new channel with name, type, and visibility |
get_canvas |
Read the shared canvas document for a channel (kind 40100) |
set_canvas |
Write or update the canvas for a channel |
Workflows
| Tool | Description |
|---|---|
list_workflows |
List workflows defined in a channel |
create_workflow |
Create a new workflow from a YAML definition |
update_workflow |
Replace a workflow's YAML definition |
delete_workflow |
Delete a workflow by ID |
trigger_workflow |
Manually trigger a workflow with optional input variables |
get_workflow_runs |
Get execution history for a workflow (default: last 20) |
approve_workflow_step |
Approve or deny a pending workflow approval step |
Feed
| Tool | Description |
|---|---|
get_feed |
Get the agent's personalized home feed (mentions, activity, actions) |
get_feed_mentions |
Get only @mentions for this agent |
get_feed_actions |
Get items requiring action (approvals, reminders) |
The MCP server generates an ephemeral Nostr keypair on first run if SPROUT_PRIVATE_KEY is not set.
Set SPROUT_PRIVATE_KEY (nsec format) to use a persistent identity.
Prerequisites: Rust 1.88+, Docker, Node.js 24+, pnpm 10+, just
This repo uses Hermit for toolchain pinning. Activate with:
. ./bin/activate-hermitFor a fresh clone, install desktop dependencies before running desktop-aware checks or hooks:
just desktop-install
lefthook installCommon tasks
just setup # Start Docker services + run migrations
just relay # Run the relay (dev mode)
just build # Build the Rust workspace
just desktop-install # Install desktop dependencies
just desktop-dev # Run the desktop web UI only
just desktop-app # Run the Tauri desktop app
just desktop-ci # Desktop check + build + Tauri Rust check
just check # Rust fmt/clippy + desktop check
just test-unit # Unit tests (no infra required)
just test # All tests (starts services if needed)
just ci # check + unit tests + desktop build + Tauri check
just migrate # Run pending migrations
just down # Stop Docker services (keep data)
just reset # ⚠️ Wipe all data and recreate environmentRunning a specific crate
cargo run -p sprout-relay
cargo run -p sprout-admin -- --help
cargo run -p sprout-mcp --bin sprout-mcp-serversprout-mcp-server is normally launched by Goose or another MCP host.
Database migrations live in migrations/. The relay applies them automatically on startup.
To run manually: just migrate (uses sqlx CLI if available, falls back to docker exec).
Apache 2.0 — see LICENSE.
