mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
docs: fix stale claims, remove counts, purge LiveKit references (#742)
Signed-off-by: tlongwell-block <109685178+tlongwell-block@users.noreply.github.com>
This commit is contained in:
@@ -78,13 +78,6 @@ RUST_LOG=sprout_relay=debug,sprout_db=debug,sprout_auth=debug,sprout_pubsub=debu
|
|||||||
# OTLP tracing endpoint (optional — leave unset to disable)
|
# OTLP tracing endpoint (optional — leave unset to disable)
|
||||||
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
|
# OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
# Huddle (LiveKit integration)
|
|
||||||
# -----------------------------------------------------------------------------
|
|
||||||
# LIVEKIT_API_KEY=devkey
|
|
||||||
# LIVEKIT_API_SECRET=devsecret
|
|
||||||
# LIVEKIT_URL=ws://localhost:7880
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# ACP (Agent Communication Protocol — sprout-acp harness)
|
# ACP (Agent Communication Protocol — sprout-acp harness)
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|||||||
33
AGENTS.md
33
AGENTS.md
@@ -10,25 +10,38 @@ code style, PR process, architecture), see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|||||||
|
|
||||||
```
|
```
|
||||||
crates/
|
crates/
|
||||||
sprout-relay # WebSocket relay server — main entry point
|
# Relay + core
|
||||||
sprout-core # Core types, event verification, filter matching
|
sprout-relay # WebSocket relay server — main entry point; also hosts git + huddle audio
|
||||||
|
sprout-core # Core types, event verification, filter matching, kind registry
|
||||||
sprout-db # Postgres event store and data access layer
|
sprout-db # Postgres event store and data access layer
|
||||||
sprout-auth # Authentication and authorization
|
sprout-auth # Authentication and authorization
|
||||||
sprout-pubsub # Redis pub/sub fan-out, presence, typing indicators
|
sprout-pubsub # Redis pub/sub fan-out, presence, typing indicators
|
||||||
sprout-mcp # MCP server providing AI agent tools
|
|
||||||
sprout-acp # ACP harness bridging Sprout events to AI agents
|
|
||||||
sprout-workflow # YAML-as-code workflow engine (evalexpr conditions)
|
|
||||||
sprout-search # Typesense-backed full-text search
|
sprout-search # Typesense-backed full-text search
|
||||||
sprout-audit # Hash-chain audit log
|
sprout-audit # Hash-chain audit log
|
||||||
sprout-huddle # LiveKit audio/video integration
|
sprout-media # Blossom/S3 media storage
|
||||||
sprout-proxy # Nostr client compatibility proxy
|
# Agent surface
|
||||||
|
sprout-mcp # MCP server providing AI agent tools (being phased out in favor of the CLI)
|
||||||
|
sprout-acp # ACP harness bridging Sprout events to AI agents
|
||||||
|
sprout-agent # Minimal ACP-compliant agent (non-streaming, tool-calls-as-output)
|
||||||
|
sprout-dev-mcp # Developer MCP server — shell + file-edit tools
|
||||||
|
sprout-persona # Agent persona packs
|
||||||
|
sprout-workflow # YAML-as-code workflow engine (evalexpr conditions)
|
||||||
|
# Clients + interop
|
||||||
|
sprout-proxy # Nostr client compatibility proxy (NIP-28)
|
||||||
|
sprout-pair-relay # Ephemeral sidecar relay for NIP-AB device pairing
|
||||||
|
sprout-pairing-cli # CLI for NIP-AB device pairing interop testing
|
||||||
|
git-sign-nostr # Sign git objects with a Nostr key
|
||||||
|
git-credential-nostr # Git credential helper for Nostr-authed push/fetch
|
||||||
|
# Tooling + shared
|
||||||
|
sprout-cli # Agent-first CLI
|
||||||
|
sprout-sdk # Typed Nostr event builders
|
||||||
sprout-admin # Operator CLI for relay administration
|
sprout-admin # Operator CLI for relay administration
|
||||||
sprout-test-client # Integration test client and E2E test suite
|
sprout-test-client # Integration test client and E2E test suite
|
||||||
sprout-sdk # Typed Nostr event builders (used by sprout-mcp and sprout-cli)
|
sprig # All-in-one harness bundling ACP, agent, and dev MCP
|
||||||
sprout-media # Blossom/S3 media storage
|
|
||||||
sprout-cli # Agent-first CLI
|
|
||||||
|
|
||||||
desktop/ # Tauri 2 + React 19 desktop app
|
desktop/ # Tauri 2 + React 19 desktop app
|
||||||
|
web/ # Browser web client (repo browser, served by the relay)
|
||||||
|
mobile/ # Flutter mobile app
|
||||||
migrations/ # SQL migrations (auto-applied on relay startup)
|
migrations/ # SQL migrations (auto-applied on relay startup)
|
||||||
scripts/ # Dev tooling
|
scripts/ # Dev tooling
|
||||||
.env.example # Config template — copy to .env before running
|
.env.example # Config template — copy to .env before running
|
||||||
|
|||||||
105
ARCHITECTURE.md
105
ARCHITECTURE.md
@@ -6,7 +6,7 @@ Sprout is a self-hosted team communication platform built on the Nostr protocol
|
|||||||
|
|
||||||
The relay is the single source of truth. All reads and writes flow through it. There is no peer-to-peer event exchange, no gossip, no replication — just clients connecting to one relay over WebSocket, and the relay enforcing auth, verifying signatures, persisting events, fanning out to subscribers, indexing for search, and triggering automation.
|
The relay is the single source of truth. All reads and writes flow through it. There is no peer-to-peer event exchange, no gossip, no replication — just clients connecting to one relay over WebSocket, and the relay enforcing auth, verifying signatures, persisting events, fanning out to subscribers, indexing for search, and triggering automation.
|
||||||
|
|
||||||
Sprout is a Rust monorepo (~72K LOC across 17 crates), licensed Apache 2.0 under Block, Inc.
|
Sprout is a Rust monorepo, licensed Apache 2.0 under Block, Inc.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -81,15 +81,14 @@ sprout-core (zero I/O — types, verification, filter matching, kind registry)
|
|||||||
sprout-mcp (agent API surface — stdio MCP server; depends on sprout-core and sprout-sdk)
|
sprout-mcp (agent API surface — stdio MCP server; depends on sprout-core and sprout-sdk)
|
||||||
sprout-acp (agent harness — bridges relay @mentions → AI agents via ACP/JSON-RPC)
|
sprout-acp (agent harness — bridges relay @mentions → AI agents via ACP/JSON-RPC)
|
||||||
sprout-proxy (NIP-28 compatibility proxy — translates standard Nostr clients ↔ Sprout relay)
|
sprout-proxy (NIP-28 compatibility proxy — translates standard Nostr clients ↔ Sprout relay)
|
||||||
sprout-huddle (LiveKit audio/video integration — standalone, not wired into relay)
|
|
||||||
sprout-sdk (typed Nostr event builders — used by sprout-mcp, sprout-acp, and sprout-cli)
|
sprout-sdk (typed Nostr event builders — used by sprout-mcp, sprout-acp, and sprout-cli)
|
||||||
sprout-media (Blossom/S3 media storage)
|
sprout-media (Blossom/S3 media storage)
|
||||||
sprout-cli (agent-first CLI)
|
sprout-cli (agent-first CLI)
|
||||||
sprout-admin (operator CLI: mint/list API tokens)
|
sprout-admin (operator CLI: relay membership + key generation)
|
||||||
sprout-test-client (integration test harness + manual CLI)
|
sprout-test-client (integration test harness + manual CLI)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Key architectural principle:** The relay is the single source of truth. `sprout-relay` orchestrates all subsystems by calling them directly — it imports `sprout-db`, `sprout-auth`, `sprout-pubsub`, `sprout-search`, `sprout-audit`, and `sprout-workflow`. However, those subsystems are isolated from each other: `sprout-workflow` never calls `sprout-pubsub`, `sprout-search` never calls `sprout-db`, etc. Cross-subsystem coordination happens only through the relay. `sprout-proxy` connects to the relay as a WebSocket client and translates NIP-28 events between standard Nostr clients and the Sprout relay. `sprout-huddle` is a standalone crate not yet wired into the relay.
|
**Key architectural principle:** The relay is the single source of truth. `sprout-relay` orchestrates all subsystems by calling them directly — it imports `sprout-db`, `sprout-auth`, `sprout-pubsub`, `sprout-search`, `sprout-audit`, and `sprout-workflow`. However, those subsystems are isolated from each other: `sprout-workflow` never calls `sprout-pubsub`, `sprout-search` never calls `sprout-db`, etc. Cross-subsystem coordination happens only through the relay. `sprout-proxy` connects to the relay as a WebSocket client and translates NIP-28 events between standard Nostr clients and the Sprout relay.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -316,7 +315,7 @@ After registering, the REQ handler queries Postgres for stored events matching t
|
|||||||
|
|
||||||
### sprout-core — Shared Types and Verification
|
### sprout-core — Shared Types and Verification
|
||||||
|
|
||||||
**1,196 LOC. Zero I/O.** The foundation every other crate builds on. Explicitly prohibits tokio, sqlx, redis, and axum in its `Cargo.toml`.
|
**Zero I/O.** The foundation every other crate builds on. Explicitly prohibits tokio, sqlx, redis, and axum in its `Cargo.toml`.
|
||||||
|
|
||||||
**Key types:**
|
**Key types:**
|
||||||
|
|
||||||
@@ -345,7 +344,7 @@ pub const ALL_KINDS: &[u32] // 80 entries (KIND_AUTH excluded — never stored)
|
|||||||
|
|
||||||
### sprout-auth — Authentication and Authorization
|
### sprout-auth — Authentication and Authorization
|
||||||
|
|
||||||
**2,310 LOC.** Handles authentication paths, scope enforcement, and token operations.
|
Handles authentication paths, scope enforcement, and token operations.
|
||||||
|
|
||||||
**Auth paths:**
|
**Auth paths:**
|
||||||
|
|
||||||
@@ -378,7 +377,7 @@ pub trait RateLimiter: Send + Sync { ... }
|
|||||||
|
|
||||||
### sprout-db — Postgres Event Store
|
### sprout-db — Postgres Event Store
|
||||||
|
|
||||||
**7,367 LOC.** All database access. Uses `sqlx::query()` (runtime, not compile-time macros) — no `.sqlx/` offline cache required.
|
All database access. Uses `sqlx::query()` (runtime, not compile-time macros) — no `.sqlx/` offline cache required.
|
||||||
|
|
||||||
**Key operations:**
|
**Key operations:**
|
||||||
|
|
||||||
@@ -416,7 +415,7 @@ pub trait RateLimiter: Send + Sync { ... }
|
|||||||
|
|
||||||
### sprout-pubsub — Redis Pub/Sub, Presence, Typing
|
### sprout-pubsub — Redis Pub/Sub, Presence, Typing
|
||||||
|
|
||||||
**887 LOC.** Manages Redis pub/sub fan-out, presence tracking, and typing indicators.
|
Manages Redis pub/sub fan-out, presence tracking, and typing indicators.
|
||||||
|
|
||||||
**Architecture:**
|
**Architecture:**
|
||||||
|
|
||||||
@@ -448,7 +447,7 @@ EXPIRE sprout:typing:{channel_id} 60
|
|||||||
|
|
||||||
### sprout-search — Typesense Integration
|
### sprout-search — Typesense Integration
|
||||||
|
|
||||||
**1,126 LOC.** Full-text search via Typesense. All HTTP calls use `reqwest` with `X-TYPESENSE-API-KEY`.
|
Full-text search via Typesense. All HTTP calls use `reqwest` with `X-TYPESENSE-API-KEY`.
|
||||||
|
|
||||||
**Collection schema (7 fields):** `id`, `content`, `kind` (int32), `pubkey` (facet), `channel_id` (facet, optional), `created_at` (int64, default sort), `tags_flat` (string[]).
|
**Collection schema (7 fields):** `id`, `content`, `kind` (int32), `pubkey` (facet), `channel_id` (facet, optional), `created_at` (int64, default sort), `tags_flat` (string[]).
|
||||||
|
|
||||||
@@ -466,7 +465,7 @@ EXPIRE sprout:typing:{channel_id} 60
|
|||||||
|
|
||||||
### sprout-audit — Hash-Chain Audit Log
|
### sprout-audit — Hash-Chain Audit Log
|
||||||
|
|
||||||
**776 LOC.** Tamper-evident append-only log with SHA-256 hash chaining.
|
Tamper-evident append-only log with SHA-256 hash chaining.
|
||||||
|
|
||||||
**Hash chain:** each entry stores `prev_hash` (hash of the previous entry). `verify_chain()` walks entries and recomputes hashes to detect tampering. Genesis entry uses `GENESIS_HASH` (64 zeros).
|
**Hash chain:** each entry stores `prev_hash` (hash of the previous entry). `verify_chain()` walks entries and recomputes hashes to detect tampering. Genesis entry uses `GENESIS_HASH` (64 zeros).
|
||||||
|
|
||||||
@@ -482,7 +481,7 @@ EXPIRE sprout:typing:{channel_id} 60
|
|||||||
|
|
||||||
### sprout-workflow — YAML-as-Code Automation Engine
|
### sprout-workflow — YAML-as-Code Automation Engine
|
||||||
|
|
||||||
**4,012 LOC.** Parses, validates, and executes channel-scoped workflow definitions.
|
Parses, validates, and executes channel-scoped workflow definitions.
|
||||||
|
|
||||||
**Workflow definition structure:**
|
**Workflow definition structure:**
|
||||||
```yaml
|
```yaml
|
||||||
@@ -533,7 +532,7 @@ Note: Both `TriggerDef` and `ActionDef` use serde internally-tagged enums. Trigg
|
|||||||
|
|
||||||
### sprout-proxy — NIP-28 Compatibility Proxy
|
### sprout-proxy — NIP-28 Compatibility Proxy
|
||||||
|
|
||||||
**4,933 LOC.** Lets standard Nostr clients (Coracle, nak, Amethyst, nostr-tools, nostr-sdk) read and write Sprout channels using the NIP-28 Public Chat Channels protocol. Connects to the relay as a WebSocket client; presents a standard NIP-01/NIP-11/NIP-28/NIP-42 interface to external clients.
|
Lets standard Nostr clients (Coracle, nak, Amethyst, nostr-tools, nostr-sdk) read and write Sprout channels using the NIP-28 Public Chat Channels protocol. Connects to the relay as a WebSocket client; presents a standard NIP-01/NIP-11/NIP-28/NIP-42 interface to external clients.
|
||||||
|
|
||||||
**Key modules:** `server.rs` (Axum WebSocket server, NIP-11, NIP-42 auth, filter splitting), `translate.rs` (bidirectional kind/tag translation), `upstream.rs` (persistent relay connection with auto-reconnect and subscription replay), `channel_map.rs` (bidirectional UUID ↔ kind:40 event ID mapping), `shadow_keys.rs` (deterministic keypair derivation), `guest_store.rs` (pubkey-based guest registry), `invite_store.rs` (token-based invite system).
|
**Key modules:** `server.rs` (Axum WebSocket server, NIP-11, NIP-42 auth, filter splitting), `translate.rs` (bidirectional kind/tag translation), `upstream.rs` (persistent relay connection with auto-reconnect and subscription replay), `channel_map.rs` (bidirectional UUID ↔ kind:40 event ID mapping), `shadow_keys.rs` (deterministic keypair derivation), `guest_store.rs` (pubkey-based guest registry), `invite_store.rs` (token-based invite system).
|
||||||
|
|
||||||
@@ -575,27 +574,23 @@ Kind 5 (deletion) is intentionally blocked inbound — the relay's deletion hand
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### sprout-huddle — LiveKit Audio/Video Integration
|
### Huddle Audio — WebSocket Opus Relay
|
||||||
|
|
||||||
**728 LOC.** Mints LiveKit JWT tokens and parses LiveKit webhook events. Defines session/participant data structures (no active registry — types only).
|
Real-time voice lives inside `sprout-relay` (`src/audio/`), not a separate crate. A WebSocket endpoint (`wss://.../huddle/{channel_id}/audio`) authenticates each participant with a NIP-42 challenge, checks channel membership, admits them to an in-memory room, and forwards opaque Opus frames between peers. No external SFU.
|
||||||
|
|
||||||
**JWT token:** HS256, 6-hour TTL (overridable). Claims: `iss` (api_key), `sub` (identity), `iat`, `exp`, `name`, `video` (VideoGrant: room, roomJoin, canPublish, canSubscribe).
|
**Frame protocol (v2):** 8-byte big-endian header (sequence `u16`, 48 kHz timestamp `u32`, level dBov `i8`, flags `u8`) followed by an opaque Opus payload. Invalid `level_dbov` values are clamped rather than dropped — losing a metric beats losing audio.
|
||||||
|
|
||||||
**Webhook verification:** HMAC-SHA256 of raw body bytes, hex-encoded. Constant-time comparison via `hmac` crate's built-in `verify_slice`.
|
**Room state:** an admission guard synchronizes joins against the room's ended flag; soft cap 25 peers (hard cap 255 via `u8` peer index). Per-peer audio uses a bounded channel (drop-on-full); the control channel is separate and never drops join/leave.
|
||||||
|
|
||||||
**5 webhook event types:** `RoomStarted`, `RoomFinished`, `ParticipantJoined`, `ParticipantLeft`, `TrackPublished`.
|
**Lifecycle events:** the relay emits Nostr events for participant joined / left and huddle ended; the desktop client emits huddle started and guidelines. When the last peer leaves, the room ends and the channel archives atomically.
|
||||||
|
|
||||||
**Session types:** `HuddleSession` with `Vec<HuddleParticipant>`. Participants have `joined_at`, `left_at`, and `Vec<TrackInfo>`. These are data structures and helpers only — no session registry or lifecycle manager exists in the crate.
|
**Not yet built:** recording and per-track publishing (the corresponding kinds are reserved, no producer exists).
|
||||||
|
|
||||||
**Room naming:** `"sprout-{uuid}"` format via `create_room_name(channel_id)`.
|
|
||||||
|
|
||||||
**Does NOT:** emit Nostr events for huddle lifecycle (relay-side integration is planned). Does NOT persist session state.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### sprout-relay — The Server
|
### sprout-relay — The Server
|
||||||
|
|
||||||
**14,327 LOC.** Axum WebSocket server. Ties all other crates together. The only crate that imports and orchestrates all subsystems.
|
Axum WebSocket server. Ties all other crates together. The only crate that imports and orchestrates all subsystems.
|
||||||
|
|
||||||
**`AppState`** (Arc-wrapped, shared across all connections — key fields shown, not exhaustive):
|
**`AppState`** (Arc-wrapped, shared across all connections — key fields shown, not exhaustive):
|
||||||
|
|
||||||
@@ -686,19 +681,9 @@ pub enum AuthState { Pending { challenge: String }, Authenticated(AuthContext),
|
|||||||
|
|
||||||
### sprout-mcp — Agent API Surface
|
### sprout-mcp — Agent API Surface
|
||||||
|
|
||||||
**4,879 LOC.** stdio MCP server using the `rmcp` SDK. The interface through which AI agents interact with Sprout. Logs to stderr (stdout is the MCP JSON-RPC channel).
|
stdio MCP server using the `rmcp` SDK. The interface through which AI agents interact with Sprout. Logs to stderr (stdout is the MCP JSON-RPC channel).
|
||||||
|
|
||||||
**43 registered tools (+ 1 implemented but unregistered, + 3 deferred media/realtime toolsets):**
|
The registered tool surface is grouped into toolsets, enabled per server via `SPROUT_TOOLSETS`. The authoritative list (and per-toolset grouping) is `ALL_TOOLS` in `crates/sprout-mcp/src/toolsets.rs` — the doc deliberately doesn't duplicate it, since a hand-copied list drifts. The default toolset covers messaging, threads, search, feed, reactions, channel basics, DMs, profiles/presence, and workflow triggers; opt-in toolsets add channel admin, canvas, workflow admin, forums, social, and media.
|
||||||
|
|
||||||
| Category | Tools |
|
|
||||||
|----------|-------|
|
|
||||||
| Default | `send_message`, `send_diff_message`, `edit_message`, `delete_message`, `get_messages`, `get_thread`, `search`, `get_feed`, `add_reaction`, `remove_reaction`, `get_reactions`, `list_channels`, `get_channel`, `join_channel`, `leave_channel`, `update_channel`, `set_channel_topic`, `set_channel_purpose`, `open_dm`, `get_users`, `set_profile`, `get_presence`, `set_presence`, `trigger_workflow`, `approve_step` |
|
|
||||||
| Channel admin | `create_channel`, `archive_channel`, `unarchive_channel`, `add_channel_member`, `remove_channel_member`, `list_channel_members`, `delete_channel` |
|
|
||||||
| DMs | `add_dm_member`, `hide_dm`, `list_dms` |
|
|
||||||
| Canvas | `get_canvas`, `set_canvas` |
|
|
||||||
| Workflow admin | `list_workflows`, `create_workflow`, `update_workflow`, `delete_workflow`, `get_workflow_runs` |
|
|
||||||
| Identity | `set_channel_add_policy` |
|
|
||||||
| Forums | `vote_on_post` |
|
|
||||||
|
|
||||||
**Key implementation details:**
|
**Key implementation details:**
|
||||||
- Connects to relay via WebSocket (`tokio_tungstenite`). Handles NIP-42 auth automatically.
|
- Connects to relay via WebSocket (`tokio_tungstenite`). Handles NIP-42 auth automatically.
|
||||||
@@ -717,7 +702,7 @@ pub enum AuthState { Pending { challenge: String }, Authenticated(AuthContext),
|
|||||||
|
|
||||||
### sprout-acp — Agent Communication Protocol Harness
|
### sprout-acp — Agent Communication Protocol Harness
|
||||||
|
|
||||||
**14,920 LOC.** Standalone binary that bridges Sprout relay events to AI agents via the [Agent Communication Protocol](https://agentclientprotocol.com/) (ACP). The active counterpart to `sprout-mcp`'s passive tool-serving role.
|
Standalone binary that bridges Sprout relay events to AI agents via the [Agent Communication Protocol](https://agentclientprotocol.com/) (ACP). The active counterpart to `sprout-mcp`'s passive tool-serving role.
|
||||||
|
|
||||||
**Architecture:**
|
**Architecture:**
|
||||||
|
|
||||||
@@ -753,23 +738,19 @@ Sprout Relay ──WS──→ sprout-acp ──stdio (ACP/JSON-RPC)──→ Ag
|
|||||||
|
|
||||||
### sprout-admin — Operator CLI
|
### sprout-admin — Operator CLI
|
||||||
|
|
||||||
**213 LOC.** Two subcommands:
|
Subcommands:
|
||||||
|
|
||||||
| Subcommand | Purpose |
|
| Subcommand | Purpose |
|
||||||
|------------|---------|
|
|------------|---------|
|
||||||
| `mint-token` | Generate API token, store SHA-256 hash in DB, display raw token once |
|
| `add-member` | Add a pubkey to the relay membership list (`--pubkey`, `--role`) |
|
||||||
| `list-tokens` | List all active tokens (ID, name, scopes, created) |
|
| `list-members` | List all relay members |
|
||||||
|
| `generate-key` | Generate a new Nostr keypair (for bootstrapping) |
|
||||||
`mint-token` options: `--name`, `--scopes` (comma-separated), optional `--pubkey`. If `--pubkey` omitted, generates a new keypair and displays `nsec` (bech32) and pubkey.
|
| `reconcile-channels` | Emit kind:39000/39002 discovery events for channels missing them (idempotent) |
|
||||||
|
|
||||||
Raw token is shown exactly once and never stored. Only the SHA-256 hash reaches the database.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### sprout-test-client — Integration Test Harness
|
### sprout-test-client — Integration Test Harness
|
||||||
|
|
||||||
**9,319 LOC** (832 in `src/`, remainder in `tests/` directory).
|
|
||||||
|
|
||||||
**`SproutTestClient`** wraps a WebSocket connection with a `VecDeque<RelayMessage>` buffer for message interleaving. Methods: `connect`, `connect_unauthenticated`, `authenticate`, `send_event`, `send_text_message`, `subscribe`, `close_subscription`, `recv_event`, `collect_until_eose`, `disconnect`.
|
**`SproutTestClient`** wraps a WebSocket connection with a `VecDeque<RelayMessage>` buffer for message interleaving. Methods: `connect`, `connect_unauthenticated`, `authenticate`, `send_event`, `send_text_message`, `subscribe`, `close_subscription`, `recv_event`, `collect_until_eose`, `disconnect`.
|
||||||
|
|
||||||
**Test coverage:**
|
**Test coverage:**
|
||||||
@@ -841,7 +822,6 @@ Applied in: `sprout-workflow` (CallWebhook action), `sprout-core` (shared utilit
|
|||||||
|
|
||||||
### Webhook Security
|
### Webhook Security
|
||||||
|
|
||||||
- LiveKit webhooks: HMAC-SHA256 of raw body bytes, hex-encoded, constant-time comparison
|
|
||||||
- Workflow webhooks: constant-time XOR comparison of stored UUID secret (not HMAC — compares the secret directly, not a body MAC)
|
- Workflow webhooks: constant-time XOR comparison of stored UUID secret (not HMAC — compares the secret directly, not a body MAC)
|
||||||
- Outbound webhooks (CallWebhook): SSRF protection + redirects disabled + 1 MiB response cap
|
- Outbound webhooks (CallWebhook): SSRF protection + redirects disabled + 1 MiB response cap
|
||||||
|
|
||||||
@@ -898,35 +878,6 @@ These are verified gaps in the current implementation — not design aspirations
|
|||||||
| 1 | **No sqlx offline query cache** | Uses `sqlx::query()` (runtime) not `sqlx::query!()` (compile-time). No `.sqlx/` directory. Queries are not validated at compile time. |
|
| 1 | **No sqlx offline query cache** | Uses `sqlx::query()` (runtime) not `sqlx::query!()` (compile-time). No `.sqlx/` directory. Queries are not validated at compile time. |
|
||||||
| 2 | **No rate limiting implementation** | `RateLimiter` trait exists in `sprout-auth`. Only implementation is `AlwaysAllowRateLimiter` (test stub, gated behind `#[cfg(any(test, feature = "test-utils"))]`). `RateLimitConfig` defines 4 tiers (human, agent-standard, agent-elevated, agent-platform) but none are enforced. |
|
| 2 | **No rate limiting implementation** | `RateLimiter` trait exists in `sprout-auth`. Only implementation is `AlwaysAllowRateLimiter` (test stub, gated behind `#[cfg(any(test, feature = "test-utils"))]`). `RateLimitConfig` defines 4 tiers (human, agent-standard, agent-elevated, agent-platform) but none are enforced. |
|
||||||
| 3 | **No dedicated typing REST endpoint** | Typing indicators (kind 20002) are delivered via both local fan-out and Redis pub/sub (cross-node). There is no REST endpoint to query current typers — `/api/presence` returns online/away status only, not typing state. |
|
| 3 | **No dedicated typing REST endpoint** | Typing indicators (kind 20002) are delivered via both local fan-out and Redis pub/sub (cross-node). There is no REST endpoint to query current typers — `/api/presence` returns online/away status only, not typing state. |
|
||||||
| 4 | **sprout-huddle is scaffolding** | `sprout-huddle` defines types, token generation, and webhook parsing, but relay-side lifecycle event emission is not implemented. Huddle state events are not wired into the relay's event pipeline. `sprout-proxy` is now functional — see its section above. |
|
| 4 | **Huddle recording/tracks not built** | Voice, room lifecycle, and join/leave/end events are wired (see Huddle Audio above). Recording and per-track publishing have reserved kinds but no producer yet. |
|
||||||
| 5 | **Approval gates not wired end-to-end** | The executor returns `StepResult::Suspended` and the relay has grant/deny API endpoints with DB CRUD, but the engine intercepts before creating `WaitingApproval` rows — runs that hit an approval gate are marked as Failed (🚧 WF-08). |
|
| 5 | **Approval gates not wired end-to-end** | The executor returns `StepResult::Suspended` and the relay has grant/deny API endpoints with DB CRUD, but the engine intercepts before creating `WaitingApproval` rows — runs that hit an approval gate are marked as Failed (🚧 WF-08). |
|
||||||
| 6 | **Workflow actions partially stubbed** | `send_dm` and `set_channel_topic` actions log intent but do not emit events (🚧 WF-07). |
|
| 6 | **Workflow actions partially stubbed** | The `send_dm` and `set_channel_topic` workflow actions are in the schema but return `NotImplemented` — a run that reaches one fails at execution (🚧 WF-07). |
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Appendix: LOC Summary
|
|
||||||
|
|
||||||
| Crate | LOC | Layer |
|
|
||||||
|-------|-----|-------|
|
|
||||||
| sprout-core | 1,196 | Foundation |
|
|
||||||
| sprout-auth | 2,310 | Foundation |
|
|
||||||
| sprout-db | 7,367 | Foundation |
|
|
||||||
| sprout-pubsub | 887 | Foundation |
|
|
||||||
| sprout-search | 1,126 | Foundation |
|
|
||||||
| sprout-audit | 776 | Foundation |
|
|
||||||
| sprout-workflow | 4,012 | Foundation |
|
|
||||||
| sprout-proxy | 4,933 | Client compatibility |
|
|
||||||
| sprout-huddle | 728 | Standalone |
|
|
||||||
| sprout-relay | 14,327 | Server |
|
|
||||||
| sprout-mcp | 4,879 | Agent API |
|
|
||||||
| sprout-acp | 14,920 | Agent harness |
|
|
||||||
| sprout-sdk | 1,237 | Shared library |
|
|
||||||
| sprout-media | 977 | Media storage |
|
|
||||||
| sprout-cli | 2,919 | Tooling |
|
|
||||||
| sprout-admin | 213 | Tooling |
|
|
||||||
| sprout-test-client | 9,319 | Tooling |
|
|
||||||
| **Total** | **~72,126** | |
|
|
||||||
|
|
||||||
*LOC counted with `find crates -name '*.rs' | xargs wc -l`. Includes tests. Measured 2026-04-05.*
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ End-to-end tests live in `crates/sprout-test-client/tests/`:
|
|||||||
Run them with (requires running infrastructure):
|
Run them with (requires running infrastructure):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo test -p sprout-test-client
|
cargo test -p sprout-test-client -- --ignored
|
||||||
```
|
```
|
||||||
|
|
||||||
See `TESTING.md` for the full multi-agent E2E testing guide.
|
See `TESTING.md` for the full multi-agent E2E testing guide.
|
||||||
@@ -296,7 +296,6 @@ sprout-acp ← ACP harness (bridges Sprout relay events to AI agents via
|
|||||||
sprout-proxy ← Nostr client compatibility layer
|
sprout-proxy ← Nostr client compatibility layer
|
||||||
sprout-sdk ← Typed Nostr event builders (used by sprout-mcp and sprout-cli)
|
sprout-sdk ← Typed Nostr event builders (used by sprout-mcp and sprout-cli)
|
||||||
sprout-media ← Blossom/S3 media storage
|
sprout-media ← Blossom/S3 media storage
|
||||||
sprout-huddle ← LiveKit integration
|
|
||||||
sprout-cli ← Agent-first CLI for interacting with the relay
|
sprout-cli ← Agent-first CLI for interacting with the relay
|
||||||
sprout-admin ← Operator CLI
|
sprout-admin ← Operator CLI
|
||||||
sprout-test-client← Integration test harness
|
sprout-test-client← Integration test harness
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.88.0"
|
rust-version = "1.88.0"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
repository = "https://github.com/sprout-rs/sprout"
|
repository = "https://github.com/block/sprout"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
# Runtime
|
# Runtime
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ A Rust workspace of focused crates. Single source of truth: the relay. See [ARCH
|
|||||||
|
|
||||||
**Services** — `sprout-db` (Postgres) · `sprout-auth` (NIP-42/98 Schnorr auth, rate limiting) · `sprout-pubsub` (Redis, presence, typing) · `sprout-search` (Typesense) · `sprout-audit` (hash-chain log)
|
**Services** — `sprout-db` (Postgres) · `sprout-auth` (NIP-42/98 Schnorr auth, rate limiting) · `sprout-pubsub` (Redis, presence, typing) · `sprout-search` (Typesense) · `sprout-audit` (hash-chain log)
|
||||||
|
|
||||||
**Agent surface** — `sprout-cli` (agent-first CLI, JSON in / JSON out) · `sprout-acp` (ACP harness for Goose/Codex/Claude Code) · `sprout-agent` (ACP agent — see [VISION_AGENT.md](VISION_AGENT.md)) · `sprout-mcp` (stdio MCP, legacy/optional) · `sprout-dev-mcp` (shell + file-edit tools) · `sprout-workflow` (YAML automation) · `sprout-persona` (agent persona packs) · `sprout-huddle` (LiveKit voice/video)
|
**Agent surface** — `sprout-cli` (agent-first CLI, JSON in / JSON out) · `sprout-acp` (ACP harness for Goose/Codex/Claude Code) · `sprout-agent` (ACP agent — see [VISION_AGENT.md](VISION_AGENT.md)) · `sprout-mcp` (stdio MCP — being phased out in favor of the CLI) · `sprout-dev-mcp` (shell + file-edit tools) · `sprout-workflow` (YAML automation) · `sprout-persona` (agent persona packs)
|
||||||
|
|
||||||
**Git & pairing** — `git-sign-nostr` / `git-credential-nostr` (nostr-signed git) · `sprout-pair-relay` / `sprout-pairing-cli` (relay pairing)
|
**Git & pairing** — `git-sign-nostr` / `git-credential-nostr` (nostr-signed git) · `sprout-pair-relay` / `sprout-pairing-cli` (relay pairing)
|
||||||
|
|
||||||
|
|||||||
22
VISION.md
22
VISION.md
@@ -68,7 +68,7 @@ New message type? New kind integer. Zero breaking changes.
|
|||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
Rust backend, TypeScript/React desktop. The server is a Cargo workspace of focused crates — relay, auth, pub/sub, search, audit, workflow engine, MCP agent interface, and more. The desktop client is a Tauri 2 app with React 19. See [README.md](README.md) for the full crate map.
|
Rust backend, TypeScript/React clients. The server is a Cargo workspace of focused crates — relay, auth, pub/sub, search, audit, workflow engine, MCP agent interface, and more. The desktop client is a Tauri 2 app with React 19; the relay also serves a browser web client (the repo browser at `myproject.com`). See [README.md](README.md) for the full crate map.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -93,13 +93,12 @@ One model. TLS in transit. At-rest encryption delegated to the storage layer (e.
|
|||||||
|
|
||||||
## Huddles
|
## Huddles
|
||||||
|
|
||||||
LiveKit SFU handles all media routing. Sprout provides rooms and tokens.
|
Real-time voice runs over a WebSocket Opus relay built into `sprout-relay`. Sprout authenticates participants (NIP-42), admits them to a room, and forwards Opus frames between peers — no external SFU.
|
||||||
|
|
||||||
- Agents join via the same WebRTC API as humans — they bring their own STT/TTS
|
- Agents join the same audio relay as humans — they bring their own STT/TTS
|
||||||
- Huddle state flows as Nostr events (started, joined, left, ended, recording available)
|
- Huddle lifecycle flows as Nostr events: started, joined, left, ended
|
||||||
- Workflows can trigger on huddle events
|
|
||||||
|
|
||||||
LiveKit token minting and kind definitions are in place. Relay-side lifecycle event emission is planned.
|
Voice, room lifecycle, and lifecycle events are wired. Recording and per-track publishing are planned.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -142,7 +141,7 @@ See [VISION_PROJECTS.md](VISION_PROJECTS.md) for the full forge vision: the proj
|
|||||||
|
|
||||||
## Agent CLI
|
## Agent CLI
|
||||||
|
|
||||||
`sprout-cli` is a 44-command agent-first CLI covering the full MCP surface. JSON-only stdout, structured errors on stderr, two-tier auth (NIP-98 keypair → dev pubkey). Agents can script the entire platform without a GUI.
|
`sprout-cli` is an agent-first CLI that mirrors and extends the MCP surface — same primitives, plus repo, upload, and canvas operations where the CLI is the canonical interface. JSON-only stdout, structured errors on stderr, two-tier auth (NIP-98 keypair → dev pubkey). Agents can script the entire platform without a GUI.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -194,18 +193,19 @@ Greenfield. Agent swarms build in parallel, integrating at the event store bound
|
|||||||
| | Area |
|
| | Area |
|
||||||
|-|------|
|
|-|------|
|
||||||
| ✅ | Core relay, auth, pub/sub, search, audit |
|
| ✅ | Core relay, auth, pub/sub, search, audit |
|
||||||
| ✅ | MCP server — 44 tools, full feature surface |
|
| ✅ | MCP server — full feature surface |
|
||||||
| ✅ | ACP agent harness — goose, codex, claude code |
|
| ✅ | ACP agent harness — goose, codex, claude code |
|
||||||
| ✅ | Desktop client (Tauri) — Stream, Home, Forum, DMs, Agents, Workflows, Search, Settings, Profiles, Presence |
|
| ✅ | Desktop client (Tauri) — Stream, Home, Forum, DMs, Agents, Workflows, Search, Settings, Profiles, Presence |
|
||||||
| ✅ | Channel features — messaging, threads, reactions, canvases, media uploads, editing, deletion, typing indicators, NIP-29, soft-delete |
|
| ✅ | Channel features — messaging, threads, reactions, canvases, media uploads, editing, deletion, typing indicators, NIP-29, soft-delete |
|
||||||
| ✅ | Workflow engine — YAML-as-code, execution traces, message/reaction/schedule/webhook triggers |
|
| ✅ | Workflow engine — YAML-as-code, execution traces, message/reaction/schedule/webhook triggers |
|
||||||
| ✅ | Identity — NIP-05, public profiles, NIP-98 auth, agent protection |
|
| ✅ | Identity — NIP-05, public profiles, NIP-98 auth, agent protection |
|
||||||
| ✅ | NIP-28 proxy — third-party Nostr clients (Coracle, nak, Amethyst) via `sprout-proxy` |
|
| ✅ | NIP-28 proxy — third-party Nostr clients (Coracle, nak, Amethyst) via `sprout-proxy` |
|
||||||
| ✅ | Agent CLI — `sprout-cli`, 44 commands, full MCP surface |
|
| ✅ | Agent CLI — `sprout-cli`, mirrors and extends the MCP surface |
|
||||||
| ✅ | Agent personas and teams — desktop-managed, built-in defaults, operator-defined |
|
| ✅ | Agent personas and teams — desktop-managed, built-in defaults, operator-defined |
|
||||||
| 🚧 | Workflow approval gates — infrastructure exists (DB, API, UI); executor doesn't persist/resume (WF-08) |
|
| 🚧 | Workflow approval gates — infrastructure exists (DB, API, UI); executor doesn't persist/resume (WF-08) |
|
||||||
| 🚧 | Huddles — LiveKit token minting in place; relay-side lifecycle events not yet wired |
|
| ✅ | Huddles — WebSocket Opus voice relay + lifecycle events (recording/tracks planned) |
|
||||||
| 📋 | Mobile clients, developer portal, push notifications, culture features |
|
| 🚧 | Mobile client — Flutter app (channels, forum, search, profile, pairing); in active development |
|
||||||
|
| 📋 | Developer portal, push notifications, culture features |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -217,16 +217,16 @@ If Sprout disappears tomorrow, your repos still work on gitworkshop.dev, your pa
|
|||||||
|---|---|
|
|---|---|
|
||||||
| Channels, forums, DMs, canvases | ✅ Ships today |
|
| Channels, forums, DMs, canvases | ✅ Ships today |
|
||||||
| Workflow engine (triggers, traces, conditional logic) | ✅ Ships today |
|
| Workflow engine (triggers, traces, conditional logic) | ✅ Ships today |
|
||||||
| 44-tool MCP server + ACP agent harness | ✅ Ships today |
|
| MCP server + ACP agent harness | ✅ Ships today |
|
||||||
| Blossom media storage (SHA-256, S3) | ✅ Ships today |
|
| Blossom media storage (SHA-256, S3) | ✅ Ships today |
|
||||||
| Approval gates | 🚧 Infrastructure exists; executor wiring in progress |
|
| Approval gates | 🚧 Infrastructure exists; executor wiring in progress |
|
||||||
| Project binding (kind:30617 + `sprout-` tags) | 📋 Designed |
|
| Project binding (kind:30617 + `sprout-` tags) | 📋 Designed |
|
||||||
| Git hosting (smart HTTP + NIP-34) | 📋 Designed |
|
| Git hosting (smart HTTP + NIP-34) | ✅ Ships today |
|
||||||
| Merge coordinator | 📋 Designed |
|
| Merge coordinator | 📋 Designed |
|
||||||
| NIP-34 issues (kind:1621) | 📋 Designed |
|
| NIP-34 issues (kind:1621) | 📋 Designed |
|
||||||
| Web-of-trust reputation | 📋 Designed |
|
| Web-of-trust reputation | 📋 Designed |
|
||||||
|
|
||||||
The collaboration platform is built. The forge is the work ahead — wiring git hosting, the merge train, and the reputation system into the surfaces that already exist. See [VISION.md](VISION.md) for the platform and [VISION_SOVEREIGN.md](VISION_SOVEREIGN.md) for the sovereign relay story.
|
The collaboration platform is built, and git hosting ships today — `git clone`/`git push` over smart HTTP with NIP-34 manifests. The forge layer above it is the work ahead — the merge train, project binding, issues, and the reputation system, wired into the surfaces that already exist. See [VISION.md](VISION.md) for the platform and [VISION_SOVEREIGN.md](VISION_SOVEREIGN.md) for the sovereign relay story.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Sprout V2 kind number registry.
|
//! Sprout V2 kind number registry.
|
||||||
//!
|
//!
|
||||||
//! Authoritative source: RESEARCH/SPROUT_KIND_REGISTRY_V2.md
|
//! This module is the authoritative source for Sprout kind numbers.
|
||||||
//! All constants are `u32` — NIP-01 specifies kind as an unsigned integer,
|
//! All constants are `u32` — NIP-01 specifies kind as an unsigned integer,
|
||||||
//! and u32 covers the full range without truncation.
|
//! and u32 covers the full range without truncation.
|
||||||
|
|
||||||
|
|||||||
@@ -61,40 +61,13 @@
|
|||||||
//!
|
//!
|
||||||
//! ## Available Tools
|
//! ## Available Tools
|
||||||
//!
|
//!
|
||||||
//! 42 tools total, organized into toolsets. Tools are organized into toolsets. Set
|
//! Tools are organized into toolsets; set `SPROUT_TOOLSETS` to control which are
|
||||||
//! `SPROUT_TOOLSETS` to control which are active (default: 25 core tools).
|
//! active. The authoritative list and per-toolset grouping is `ALL_TOOLS` in
|
||||||
//!
|
//! [`toolsets`] — this doc deliberately doesn't duplicate it, since a hand-copied
|
||||||
//! ### Messaging (default toolset)
|
//! list drifts. The default toolset covers messaging, threads, search, feed,
|
||||||
//! - **`send_message`** — Post a message to a channel.
|
//! reactions, channel basics, DMs, profiles/presence, and workflow triggers;
|
||||||
//! - **`send_diff_message`** — Post a diff-formatted message.
|
//! opt-in toolsets add channel admin, canvas, workflow admin, forums, social,
|
||||||
//! - **`edit_message`** — Edit an existing message.
|
//! and media.
|
||||||
//! - **`delete_message`** — Delete a message.
|
|
||||||
//! - **`get_messages`** — Fetch recent messages from a channel (default 50, max 200).
|
|
||||||
//! - **`get_thread`** — Fetch replies in a message thread.
|
|
||||||
//! - **`search`** — Full-text search across channels.
|
|
||||||
//! - **`get_feed`** — Retrieve the agent's personalized home feed (mentions, needs-action
|
|
||||||
//! items, channel activity). Replaces the former `get_feed_mentions` / `get_feed_actions`.
|
|
||||||
//! - **`add_reaction`** / **`remove_reaction`** / **`get_reactions`** — Emoji reactions.
|
|
||||||
//!
|
|
||||||
//! ### Channels (default toolset)
|
|
||||||
//! - **`list_channels`** / **`get_channel`** — List or inspect channels.
|
|
||||||
//! - **`join_channel`** / **`leave_channel`** — Membership management.
|
|
||||||
//! - **`update_channel`** / **`set_channel_topic`** / **`set_channel_purpose`** — Metadata.
|
|
||||||
//! - **`open_dm`** — Open a direct-message channel.
|
|
||||||
//! - **`list_channel_members`** — List members of a channel.
|
|
||||||
//!
|
|
||||||
//! ### Channel Admin (`channel_admin` toolset)
|
|
||||||
//! - **`create_channel`** / **`archive_channel`** / **`unarchive_channel`**
|
|
||||||
//! - **`add_channel_member`** / **`remove_channel_member`**
|
|
||||||
//!
|
|
||||||
//! ### Canvas (`canvas` toolset)
|
|
||||||
//! - **`get_canvas`** — Retrieve the shared canvas document for a channel.
|
|
||||||
//! - **`set_canvas`** — Write or replace the canvas document for a channel.
|
|
||||||
//!
|
|
||||||
//! ### Workflows
|
|
||||||
//! - **`trigger_workflow`** / **`approve_step`** — Trigger and approve steps (default toolset).
|
|
||||||
//! - **`list_workflows`** / **`create_workflow`** / **`update_workflow`** /
|
|
||||||
//! **`delete_workflow`** / **`get_workflow_runs`** — Workflow admin (`workflow_admin` toolset).
|
|
||||||
//!
|
//!
|
||||||
//! ## Example Configuration (Claude Desktop)
|
//! ## Example Configuration (Claude Desktop)
|
||||||
//!
|
//!
|
||||||
@@ -113,7 +86,7 @@
|
|||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! [Sprout]: https://github.com/sprout-rs/sprout
|
//! [Sprout]: https://github.com/block/sprout
|
||||||
//! [NIP-42]: https://github.com/nostr-protocol/nips/blob/master/42.md
|
//! [NIP-42]: https://github.com/nostr-protocol/nips/blob/master/42.md
|
||||||
|
|
||||||
// NOTE: `parse_relay_message`, `OkResponse`, and `RelayMessage` from `relay_client`
|
// NOTE: `parse_relay_message`, `OkResponse`, and `RelayMessage` from `relay_client`
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Parser and loader for Sprout persona pack files (.persona.md)"
|
description = "Parser and loader for Sprout persona pack files (.persona.md)"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
repository = "https://github.com/sprout-rs/sprout"
|
repository = "https://github.com/block/sprout"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ impl RelayInfo {
|
|||||||
pubkey: None,
|
pubkey: None,
|
||||||
contact: None,
|
contact: None,
|
||||||
supported_nips,
|
supported_nips,
|
||||||
software: "https://github.com/sprout-rs/sprout".to_string(),
|
software: "https://github.com/block/sprout".to_string(),
|
||||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
limitation: Some(relay_limitation()),
|
limitation: Some(relay_limitation()),
|
||||||
relay_self: relay_self.map(|s| s.to_string()),
|
relay_self: relay_self.map(|s| s.to_string()),
|
||||||
|
|||||||
Reference in New Issue
Block a user