From c71948d9084504037df8ae3fe3f07cc6f75f7bc0 Mon Sep 17 00:00:00 2001 From: Wes Date: Thu, 11 Jun 2026 10:12:23 -0600 Subject: [PATCH] docs: finish Buzz rename cleanup Update user-facing docs and scripts to use Buzz branding where the repo has already moved from Sprout names. Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co> Signed-off-by: Wes --- .github/workflows/sprig.yml | 2 +- AGENTS.md | 94 +++++++++---------- ARCHITECTURE.md | 136 +++++++++++++-------------- CONTRIBUTING.md | 54 +++++------ NOSTR.md | 96 +++++++++---------- README.md | 34 +++---- SECURITY.md | 6 +- TESTING.md | 146 ++++++++++++++--------------- examples/README.md | 8 +- examples/countdown-bot/README.md | 14 +-- examples/meadow-core/README.md | 8 +- scripts/cleanup-instance-agents.sh | 8 +- 12 files changed, 303 insertions(+), 303 deletions(-) diff --git a/.github/workflows/sprig.yml b/.github/workflows/sprig.yml index cd05d8597..7b2f1d433 100644 --- a/.github/workflows/sprig.yml +++ b/.github/workflows/sprig.yml @@ -1,7 +1,7 @@ name: Sprig # Builds and publishes Sprig — one deploy-anywhere Linux multicall binary for: -# buzz-acp ACP harness that bridges Sprout events to the LLM agent +# buzz-acp ACP harness that bridges Buzz events to the LLM agent # buzz-agent ACP-compliant agent (spawns MCP, calls LLMs) # buzz-dev-mcp Developer MCP server (multicall: rg, tree, buzz, # git-credential-nostr, git-sign-nostr) diff --git a/AGENTS.md b/AGENTS.md index 34bf54158..7fb96adb7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # AGENTS.md — AI Agent Contributor Guide -This guide is for AI agents contributing to the Sprout codebase. It covers +This guide is for AI agents contributing to the Buzz codebase. It covers agent-specific context and conventions. For general contributor info (setup, code style, PR process, architecture), see [CONTRIBUTING.md](CONTRIBUTING.md). @@ -8,7 +8,7 @@ code style, PR process, architecture), see [CONTRIBUTING.md](CONTRIBUTING.md). ## Ecosystem -Sprout spans five repos. This one (`block/sprout`) is the OSS source for the relay, desktop, mobile, and CLI. The others handle internal builds and deployment: +Buzz spans five repos. This one (`block/sprout`) is the OSS source for the relay, desktop, mobile, and CLI. The others handle internal builds and deployment: | Repo | Purpose | |------|---------| @@ -37,32 +37,32 @@ access information. ``` crates/ # Relay + core - 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-auth # Authentication and authorization - sprout-pubsub # Redis pub/sub fan-out, presence, typing indicators - sprout-search # Typesense-backed full-text search - sprout-audit # Hash-chain audit log - sprout-media # Blossom/S3 media storage + buzz-relay # WebSocket relay server — main entry point; also hosts git + huddle audio + buzz-core # Core types, event verification, filter matching, kind registry + buzz-db # Postgres event store and data access layer + buzz-auth # Authentication and authorization + buzz-pubsub # Redis pub/sub fan-out, presence, typing indicators + buzz-search # Typesense-backed full-text search + buzz-audit # Hash-chain audit log + buzz-media # Blossom/S3 media storage # Agent surface - 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) + buzz-acp # ACP harness bridging Buzz events to AI agents + buzz-agent # Minimal ACP-compliant agent (non-streaming, tool-calls-as-output) + buzz-dev-mcp # Developer MCP server — shell + file-edit tools + buzz-persona # Agent persona packs + buzz-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 + buzz-proxy # Nostr client compatibility proxy (NIP-28) + buzz-pair-relay # Ephemeral sidecar relay for NIP-AB device pairing + buzz-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-ws-client # Shared NIP-42 WebSocket client (connect, auth, publish) - sprout-test-client # Integration test client and E2E test suite + buzz-cli # Agent-first CLI + buzz-sdk # Typed Nostr event builders + buzz-admin # Operator CLI for relay administration + buzz-ws-client # Shared NIP-42 WebSocket client (connect, auth, publish) + buzz-test-client # Integration test client and E2E test suite sprig # All-in-one harness bundling ACP, agent, and dev MCP desktop/ # Tauri 2 + React 19 desktop app @@ -94,8 +94,8 @@ See CONTRIBUTING.md for full setup details and dependency requirements. Run `just ci` before every PR — it runs `fmt` + `clippy` + desktop lint + unit tests + builds. Clippy passing does not mean fmt passes; run both. -Run `just test` for integration tests if you touched `sprout-relay`, -`sprout-db`, or `sprout-auth` — these require a running Postgres and Redis. +Run `just test` for integration tests if you touched `buzz-relay`, +`buzz-db`, or `buzz-auth` — these require a running Postgres and Redis. **Pre-commit hooks** are installed automatically by `just setup` and auto-fix formatting via `stage_fixed`. Pre-commit runs fix variants in parallel (Rust @@ -116,13 +116,13 @@ Additional rules: ## Key Patterns -**Dual API surface**: Sprout exposes both a REST API and a NIP-29 WebSocket -relay. Both paths converge on shared DB functions in `sprout-db`. When adding +**Dual API surface**: Buzz exposes both a REST API and a NIP-29 WebSocket +relay. Both paths converge on shared DB functions in `buzz-db`. When adding a feature, implement the shared DB logic first, then wire up both surfaces. **Prefer Nostr events over new REST endpoints**: For new feature work, model -the operation as a Nostr event (new kind in `sprout-core/src/kind.rs`, handler -in `sprout-relay`) rather than adding a new REST endpoint. REST is reserved +the operation as a Nostr event (new kind in `buzz-core/src/kind.rs`, handler +in `buzz-relay`) rather than adding a new REST endpoint. REST is reserved for things that genuinely need an HTTP-only surface: media upload/download (Blossom), OAuth callbacks, health checks, and the existing read endpoints that proxy DB queries. Two helpful endpoints already exist and rarely need @@ -130,7 +130,7 @@ to be duplicated: - `POST /events` — submit any signed event (same path the WebSocket uses). - `POST /query` — Nostr REQ filters over HTTP. NIP-50 `search` filters - are routed to `sprout-search` (Typesense-backed) automatically. + are routed to `buzz-search` (Typesense-backed) automatically. - `POST /count` — Nostr COUNT filters over HTTP. If you find yourself reaching for a new REST endpoint, first check whether @@ -140,15 +140,15 @@ fan-out, NIP-29 scoping, and the existing auth pipeline for free. Reference https://github.com/nostr-protocol/nips **Event kinds**: All event kind integers are defined in -`sprout-core/src/kind.rs`. New features get new kind integers — add them here +`buzz-core/src/kind.rs`. New features get new kind integers — add them here first, then implement handling in the relay. **Channel scoping**: Channels use `h` tags (NIP-29 group tag), not `e` tags. Filters and queries must scope to `h` tags when operating within a channel. -**Agent-facing operations go in `sprout-cli`**: New agent-facing features belong in `sprout-cli` — add a subcommand there first, then wire the REST/WebSocket call in `client.rs`. `sprout-dev-mcp` (shell + file tools for `sprout-agent`) is separate. +**Agent-facing operations go in `buzz-cli`**: New agent-facing features belong in `buzz-cli` — add a subcommand there first, then wire the REST/WebSocket call in `client.rs`. `buzz-dev-mcp` (shell + file tools for `buzz-agent`) is separate. -**Workflow conditions**: `sprout-workflow` uses +**Workflow conditions**: `buzz-workflow` uses [evalexpr](https://docs.rs/evalexpr) for condition evaluation. Keep expressions simple and testable. @@ -158,20 +158,20 @@ check existing reply handlers for the pattern. --- -## Agent CLI (`sprout-cli`) +## Agent CLI (`buzz-cli`) -`sprout` is the agent-first CLI. Auth env vars -(`SPROUT_RELAY_URL`, `SPROUT_PRIVATE_KEY`, `SPROUT_AUTH_TAG`) are auto-injected +`buzz` is the agent-first CLI. Auth env vars +(`BUZZ_RELAY_URL`, `BUZZ_PRIVATE_KEY`, `BUZZ_AUTH_TAG`) are auto-injected by the ACP harness into managed agent subprocesses. In development, set -`SPROUT_PRIVATE_KEY` and `SPROUT_RELAY_URL` in your environment manually. +`BUZZ_PRIVATE_KEY` and `BUZZ_RELAY_URL` in your environment manually. ### Building the CLI ```bash -cargo build --release -p sprout-cli +cargo build --release -p buzz-cli ``` -Binary location: `./target/release/sprout`. Add `./target/release` to `PATH` +Binary location: `./target/release/buzz`. Add `./target/release` to `PATH` or invoke with the full path. ### Deep Links @@ -180,7 +180,7 @@ or invoke with the full path. thread. To read the linked thread: ```bash -sprout messages thread --channel --event --format compact +buzz messages thread --channel --event --format compact ``` Extract `channel` and `id` from the URL query parameters. The optional @@ -192,9 +192,9 @@ All reads return sig-stripped JSON arrays; all writes return 0=ok, 1=input error, 2=network/relay, 3=auth, 4=other, 5=write conflict (NIP-33 LWW). `--format compact` is a **global** flag — it goes before the subcommand: -`sprout --format compact channels list`, NOT `sprout channels list --format compact`. +`buzz --format compact channels list`, NOT `buzz channels list --format compact`. -See `crates/sprout-cli/TESTING.md` for the full live-testing runbook. +See `crates/buzz-cli/TESTING.md` for the full live-testing runbook. --- @@ -205,7 +205,7 @@ just test-unit # unit tests, no infrastructure needed just test # full integration suite (requires Postgres + Redis) ``` -E2E tests live in `crates/sprout-test-client/tests/`: +E2E tests live in `crates/buzz-test-client/tests/`: - `e2e_relay.rs` — WebSocket relay protocol - `e2e_rest_api.rs` — REST endpoint coverage - `e2e_tokens.rs` — auth token flows @@ -220,7 +220,7 @@ See [TESTING.md](TESTING.md) for the full multi-agent E2E guide. ### Desktop Screenshots (Playwright) -> **Do NOT use `sprout upload`, the relay media endpoint, or any third-party +> **Do NOT use `buzz upload`, the relay media endpoint, or any third-party > image host for PR screenshots.** Relay media URLs fail through GitHub's camo > proxy. Always use `scripts/post-screenshots.sh` — see the `desktop-screenshot` > skill for the full workflow. @@ -246,7 +246,7 @@ Output is a PNG path on stdout. Use `--messages` to inject content into a channel before capture. The JSON file is an array of objects — `channelName` and `content` are required, all other -fields are optional and passed through to `__SPROUT_E2E_EMIT_MOCK_MESSAGE__`: +fields are optional and passed through to `__BUZZ_E2E_EMIT_MOCK_MESSAGE__`: ```json [ @@ -350,7 +350,7 @@ must run BEFORE `installMockBridge(page)` — React reads state on mount, the bridge triggers mount. **Live messages:** Call `waitForMockLiveSubscription(page, channelName)` before -`__SPROUT_E2E_EMIT_MOCK_MESSAGE__` — messages are silently dropped without a +`__BUZZ_E2E_EMIT_MOCK_MESSAGE__` — messages are silently dropped without a subscription. Navigate to the channel first (triggers subscription), then away (so unread indicators appear), then inject. @@ -393,7 +393,7 @@ description. See [PR #803](https://github.com/block/sprout/pull/803). ## Common Gotchas -1. **Kind `39000` for channel metadata, not `41`** — kind 41 is NIP-01 (unused). All kinds defined in `sprout-core/src/kind.rs`. +1. **Kind `39000` for channel metadata, not `41`** — kind 41 is NIP-01 (unused). All kinds defined in `buzz-core/src/kind.rs`. 2. **Relay queries must specify `kinds`** — omitting `kinds` triggers the p-gate (403). Always include explicit kind filters. 3. **`messages search` must include `--kinds`** — an open-ended search (no kinds) hits the relay p-gate and returns 403. Pass at least `--kinds 9,45001,45003` to scope the query. 4. **Worktrees: `cd` in the same command** — shell CWD doesn't persist between tool calls. Use `cd /path && cargo build` as one command. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index e47b26ea1..25a089e8f 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,12 +1,12 @@ -# Sprout Architecture +# Buzz Architecture ## 1. Executive Summary -Sprout is a self-hosted team communication platform built on the Nostr protocol (NIP-01 wire format), where AI agents and humans are first-class equals. Every action — a chat message, a reaction, a workflow step, a canvas update, a huddle event — is a cryptographically signed Nostr event identified by a `kind` integer. Adding a new feature means defining a new kind number; existing clients see nothing and break nothing. +Buzz is a self-hosted team communication platform built on the Nostr protocol (NIP-01 wire format), where AI agents and humans are first-class equals. Every action — a chat message, a reaction, a workflow step, a canvas update, a huddle event — is a cryptographically signed Nostr event identified by a `kind` integer. Adding a new feature means defining a new kind number; existing clients see nothing and break nothing. 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, licensed Apache 2.0 under Block, Inc. +Buzz is a Rust monorepo, licensed Apache 2.0 under Block, Inc. --- @@ -16,14 +16,14 @@ Sprout is a Rust monorepo, licensed Apache 2.0 under Block, Inc. ┌─────────────────────────────────────────────────────────────────────┐ │ CLIENTS │ │ │ -│ Human (Nostr app, web, mobile) Agent (CLI tools via sprout-cli) │ +│ Human (Nostr app, web, mobile) Agent (CLI tools via buzz-cli) │ │ │ │ │ │ └──────────── WebSocket ─────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ -│ sprout-relay (Axum) │ +│ buzz-relay (Axum) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────────────────┐ │ │ │ NIP-42 │ │ EVENT │ │ REQ │ │ REST API │ │ @@ -56,7 +56,7 @@ Sprout is a Rust monorepo, licensed Apache 2.0 under Block, Inc. (multi-node fan-out wired; local-echo dedup via AppState.local_event_ids). ┌──────────────┐ - │ Typesense │ ← sprout-search (bounded worker queue) + │ Typesense │ ← buzz-search (bounded worker queue) │ (full-text │ │ search) │ └──────────────┘ @@ -67,33 +67,33 @@ Sprout is a Rust monorepo, licensed Apache 2.0 under Block, Inc. ### Crate Dependency Hierarchy ``` -sprout-core (zero I/O — types, verification, filter matching, kind registry) +buzz-core (zero I/O — types, verification, filter matching, kind registry) │ - ├── sprout-db (Postgres: events, channels, tokens, workflows, audit) - ├── sprout-auth (NIP-42, NIP-98, API tokens, scopes, rate limiting) - ├── sprout-pubsub (Redis pub/sub, presence, typing indicators) - ├── sprout-search (Typesense: index, query, delete) - ├── sprout-audit (hash-chain tamper-evident log) - └── sprout-workflow (YAML-as-code automation engine) + ├── buzz-db (Postgres: events, channels, tokens, workflows, audit) + ├── buzz-auth (NIP-42, NIP-98, API tokens, scopes, rate limiting) + ├── buzz-pubsub (Redis pub/sub, presence, typing indicators) + ├── buzz-search (Typesense: index, query, delete) + ├── buzz-audit (hash-chain tamper-evident log) + └── buzz-workflow (YAML-as-code automation engine) │ - └── sprout-relay (ties everything together — the server) - -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-sdk (typed Nostr event builders — used by sprout-acp and sprout-cli) -sprout-media (Blossom/S3 media storage) -sprout-cli (agent-first CLI) -sprout-admin (operator CLI: relay membership + key generation) -sprout-test-client (integration test harness + manual CLI) + └── buzz-relay (ties everything together — the server) + +buzz-acp (agent harness — bridges relay @mentions → AI agents via ACP/JSON-RPC) +buzz-proxy (NIP-28 compatibility proxy — translates standard Nostr clients ↔ Buzz relay) +buzz-sdk (typed Nostr event builders — used by buzz-acp and buzz-cli) +buzz-media (Blossom/S3 media storage) +buzz-cli (agent-first CLI) +buzz-admin (operator CLI: relay membership + key generation) +buzz-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. +**Key architectural principle:** The relay is the single source of truth. `buzz-relay` orchestrates all subsystems by calling them directly — it imports `buzz-db`, `buzz-auth`, `buzz-pubsub`, `buzz-search`, `buzz-audit`, and `buzz-workflow`. However, those subsystems are isolated from each other: `buzz-workflow` never calls `buzz-pubsub`, `buzz-search` never calls `buzz-db`, etc. Cross-subsystem coordination happens only through the relay. `buzz-proxy` connects to the relay as a WebSocket client and translates NIP-28 events between standard Nostr clients and the Buzz relay. --- ## 2. The Protocol -Sprout uses Nostr NIP-01 on the wire. Every action is a JSON event with six fields: +Buzz uses Nostr NIP-01 on the wire. Every action is a JSON event with six fields: ```json { @@ -116,9 +116,9 @@ The `kind` integer is the only dispatch switch. The relay routes, stores, and fa | 10000–19999 | Replaceable events (NIP-16) | | 20000–29999 | Ephemeral events — not stored, not audited | | 30000–39999 | Parameterized replaceable events | -| 40000–49999 | Sprout custom kinds | +| 40000–49999 | Buzz custom kinds | -### Sprout Custom Kinds (selected) +### Buzz Custom Kinds (selected) | Kind | Name | Description | |------|------|-------------| @@ -132,9 +132,9 @@ The `kind` integer is the only dispatch switch. The relay routes, stores, and fa | 46001–46012 | KIND_WORKFLOW_* | Workflow execution events | | 20001 | KIND_PRESENCE_UPDATE | Ephemeral presence heartbeat | -`sprout-core` defines all 81 kinds as `pub const KIND_*: u32` and exports `ALL_KINDS: &[u32]`. Kinds are `u32` (NIP-01 specifies unsigned integer; `u32` covers the full range). Sprout uses both standard Nostr kinds (e.g., kind 7 for reactions) and custom ranges (40000+). +`buzz-core` defines all 81 kinds as `pub const KIND_*: u32` and exports `ALL_KINDS: &[u32]`. Kinds are `u32` (NIP-01 specifies unsigned integer; `u32` covers the full range). Buzz uses both standard Nostr kinds (e.g., kind 7 for reactions) and custom ranges (40000+). -Note: `KIND_AUTH` (22242) is `pub const KIND_AUTH: u32` in `sprout-core/src/kind.rs` and imported by `sprout-relay/src/handlers/event.rs`. `KIND_CANVAS` (40100) is likewise `pub const KIND_CANVAS: u32` in `sprout-core/src/kind.rs`. +Note: `KIND_AUTH` (22242) is `pub const KIND_AUTH: u32` in `buzz-core/src/kind.rs` and imported by `buzz-relay/src/handlers/event.rs`. `KIND_CANVAS` (40100) is likewise `pub const KIND_CANVAS: u32` in `buzz-core/src/kind.rs`. ### Wire Protocol (NIP-01 messages) @@ -224,7 +224,7 @@ Steps 10–12 are fire-and-forget. Search indexing is sent to a bounded worker q Step 9 (fan-out) explicitly **excludes** global subscriptions (no `channel_id` constraint) from channel-scoped events — global subscriptions do NOT receive events from private channels, regardless of filter match. This is a deliberate security boundary: only subscriptions scoped to an accessible `channel_id` receive those events. -Workflow loop prevention: workflow execution kinds (46001–46012), relay-signed messages with `sprout:workflow` tag, and `KIND_GIFT_WRAP` are excluded from triggering workflows. All other stored events (including kind 9 stream messages) trigger workflow evaluation. +Workflow loop prevention: workflow execution kinds (46001–46012), relay-signed messages with `buzz:workflow` tag, and `KIND_GIFT_WRAP` are excluded from triggering workflows. All other stored events (including kind 9 stream messages) trigger workflow evaluation. ### Ephemeral Sub-Pipeline (kinds 20000–29999) @@ -312,7 +312,7 @@ After registering, the REQ handler queries Postgres for stored events matching t ## 6. Crate Reference -### sprout-core — Shared Types and Verification +### buzz-core — Shared Types and Verification **Zero I/O.** The foundation every other crate builds on. Explicitly prohibits tokio, sqlx, redis, and axum in its `Cargo.toml`. @@ -341,7 +341,7 @@ pub const ALL_KINDS: &[u32] // 80 entries (KIND_AUTH excluded — never stored) --- -### sprout-auth — Authentication and Authorization +### buzz-auth — Authentication and Authorization Handles authentication paths, scope enforcement, and token operations. @@ -368,13 +368,13 @@ pub trait RateLimiter: Send + Sync { ... } **Security details:** - NIP-98 auth: Schnorr-signed `kind:27235` events with URL + method tags. - NIP-42 timestamp tolerance: ±60 seconds. -- Dev-only key derivation: `SHA-256("sprout-test-key:{username}")` — gated behind `#[cfg(any(test, feature = "dev"))]`. The `dev` feature must not be enabled in production relay deployments. +- Dev-only key derivation: `SHA-256("buzz-test-key:{username}")` — gated behind `#[cfg(any(test, feature = "dev"))]`. The `dev` feature must not be enabled in production relay deployments. **Does NOT:** implement `RateLimiter` beyond a test stub (`AlwaysAllowRateLimiter`, gated behind `#[cfg(any(test, feature = "test-utils"))]`). No Redis-backed rate limiter exists anywhere in the codebase — rate limiting is not currently enforced. `RateLimitConfig` defines 4 tiers (human, agent-standard, agent-elevated, agent-platform) as a design target. --- -### sprout-db — Postgres Event Store +### buzz-db — Postgres Event Store All database access. Uses `sqlx::query()` (runtime, not compile-time macros) — no `.sqlx/` offline cache required. @@ -412,31 +412,31 @@ All database access. Uses `sqlx::query()` (runtime, not compile-time macros) — --- -### sprout-pubsub — Redis Pub/Sub, Presence, Typing +### buzz-pubsub — Redis Pub/Sub, Presence, Typing Manages Redis pub/sub fan-out, presence tracking, and typing indicators. **Architecture:** ``` -Publisher → pool connection → PUBLISH sprout:channel:{uuid} -Subscriber → dedicated PubSub → PSUBSCRIBE sprout:channel:* +Publisher → pool connection → PUBLISH buzz:channel:{uuid} +Subscriber → dedicated PubSub → PSUBSCRIBE buzz:channel:* → broadcast::channel(4096) ``` The subscriber uses a **dedicated** `redis::aio::PubSub` connection — not from the pool. This is intentional: pool connections cannot hold `PSUBSCRIBE` state. -**Current state:** The subscriber loop is spawned in `sprout-relay/src/main.rs` and populates the broadcast channel. A consumer task subscribes via `pubsub.subscribe_local()`, calls `sub_registry.fan_out()` on each received event, and delivers matches to local WebSocket connections via `conn_manager.send_to()`. Multi-node fan-out is now wired end-to-end. Local-echo deduplication is implemented via `AppState.local_event_ids` — events published by the local relay instance are tracked and skipped when received via the Redis round-trip. +**Current state:** The subscriber loop is spawned in `buzz-relay/src/main.rs` and populates the broadcast channel. A consumer task subscribes via `pubsub.subscribe_local()`, calls `sub_registry.fan_out()` on each received event, and delivers matches to local WebSocket connections via `conn_manager.send_to()`. Multi-node fan-out is now wired end-to-end. Local-echo deduplication is implemented via `AppState.local_event_ids` — events published by the local relay instance are tracked and skipped when received via the Redis round-trip. **Reconnection:** exponential backoff 1s → 30s (`backoff_secs * 2`). Backoff resets to 1s only after a clean stream end, not on each reconnect attempt. -**Presence:** `SET sprout:presence:{pubkey_hex} {status} EX 90` — 90-second TTL (3× the 30-second heartbeat interval). Single missed heartbeat does not cause presence flap. +**Presence:** `SET buzz:presence:{pubkey_hex} {status} EX 90` — 90-second TTL (3× the 30-second heartbeat interval). Single missed heartbeat does not cause presence flap. **Typing indicators:** ``` -ZADD sprout:typing:{channel_id} {now_unix} {pubkey_hex} -ZREMRANGEBYSCORE sprout:typing:{channel_id} -inf {now - 5.0} -EXPIRE sprout:typing:{channel_id} 60 +ZADD buzz:typing:{channel_id} {now_unix} {pubkey_hex} +ZREMRANGEBYSCORE buzz:typing:{channel_id} -inf {now - 5.0} +EXPIRE buzz:typing:{channel_id} 60 ``` 5-second activity window. 60-second key TTL prevents orphaned empty sets. @@ -444,7 +444,7 @@ EXPIRE sprout:typing:{channel_id} 60 --- -### sprout-search — Typesense Integration +### buzz-search — Typesense Integration Full-text search via Typesense. All HTTP calls use `reqwest` with `X-TYPESENSE-API-KEY`. @@ -456,13 +456,13 @@ Full-text search via Typesense. All HTTP calls use `reqwest` with `X-TYPESENSE-A - Upsert indexing: `POST /documents?action=upsert` (single), `POST /documents/import?action=upsert` (batch JSONL). - `delete_event()` validates event ID (64-char hex) before constructing the URL — prevents path injection. - `delete_event()` is idempotent: 404 treated as success. -- Permission filtering is **caller's responsibility** — `sprout-search` provides the `filter_by` mechanism but does not enforce access policy. +- Permission filtering is **caller's responsibility** — `buzz-search` provides the `filter_by` mechanism but does not enforce access policy. **Does NOT:** enforce channel membership or access control. Does NOT store events in Postgres. --- -### sprout-audit — Hash-Chain Audit Log +### buzz-audit — Hash-Chain Audit Log Tamper-evident append-only log with SHA-256 hash chaining. @@ -478,7 +478,7 @@ Tamper-evident append-only log with SHA-256 hash chaining. --- -### sprout-workflow — YAML-as-Code Automation Engine +### buzz-workflow — YAML-as-Code Automation Engine Parses, validates, and executes channel-scoped workflow definitions. @@ -529,9 +529,9 @@ Note: Both `TriggerDef` and `ActionDef` use serde internally-tagged enums. Trigg --- -### sprout-proxy — NIP-28 Compatibility Proxy +### buzz-proxy — NIP-28 Compatibility Proxy -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 Buzz 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). @@ -539,11 +539,11 @@ Lets standard Nostr clients (Coracle, nak, Amethyst, nostr-tools, nostr-sdk) rea **Kind translation (lossy):** -`KindTranslator` defines the full mapping between standard Nostr kinds and Sprout kinds. The proxy's event paths gate which kinds actually flow through — only a subset is accepted inbound or emitted outbound. +`KindTranslator` defines the full mapping between standard Nostr kinds and Buzz kinds. The proxy's event paths gate which kinds actually flow through — only a subset is accepted inbound or emitted outbound. *Inbound (client → relay) — accepted kinds:* -| Standard Kind | Sprout Kind | Note | +| Standard Kind | Buzz Kind | Note | |--------------|-------------|------| | 1, 42 | KIND_STREAM_MESSAGE | Multiple → one (lossy) | | 41 | KIND_STREAM_MESSAGE_EDIT | Channel message edit | @@ -553,7 +553,7 @@ Kind 5 (deletion) is intentionally blocked inbound — the relay's deletion hand *Outbound (relay → client) — emitted kinds:* -| Sprout Kind | Standard Kind | Note | +| Buzz Kind | Standard Kind | Note | |-------------|--------------|------| | KIND_STREAM_MESSAGE | 42 | NIP-28 channel message | | KIND_STREAM_MESSAGE_V2 | 42 | Rich format collapses to plain kind:42 | @@ -575,7 +575,7 @@ Kind 5 (deletion) is intentionally blocked inbound — the relay's deletion hand ### Huddle Audio — WebSocket Opus Relay -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. +Real-time voice lives inside `buzz-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. **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. @@ -587,7 +587,7 @@ Real-time voice lives inside `sprout-relay` (`src/audio/`), not a separate crate --- -### sprout-relay — The Server +### buzz-relay — The Server Axum WebSocket server. Ties all other crates together. The only crate that imports and orchestrates all subsystems. @@ -678,17 +678,17 @@ pub enum AuthState { Pending { challenge: String }, Authenticated(AuthContext), --- -### sprout-acp — Agent Communication Protocol Harness +### buzz-acp — Agent Communication Protocol Harness -Standalone binary that bridges Sprout relay events to AI agents via the [Agent Communication Protocol](https://agentclientprotocol.com/) (ACP). +Standalone binary that bridges Buzz relay events to AI agents via the [Agent Communication Protocol](https://agentclientprotocol.com/) (ACP). **Architecture:** ``` -Sprout Relay ──WS──→ sprout-acp ──stdio (ACP/JSON-RPC)──→ Agent (goose/codex/claude) +Buzz Relay ──WS──→ buzz-acp ──stdio (ACP/JSON-RPC)──→ Agent (goose/codex/claude) ``` -`sprout-acp` spawns AI agent subprocesses (1–32, default 1), connects to the relay via WebSocket with NIP-42 auth, discovers channels via REST API, and queues `@mention` events per channel. At most one prompt is in-flight per channel. Queued events are batched into a single prompt sent via `session/prompt` over ACP. +`buzz-acp` spawns AI agent subprocesses (1–32, default 1), connects to the relay via WebSocket with NIP-42 auth, discovers channels via REST API, and queues `@mention` events per channel. At most one prompt is in-flight per channel. Queued events are batched into a single prompt sent via `session/prompt` over ACP. **Key modules:** @@ -706,13 +706,13 @@ Sprout Relay ──WS──→ sprout-acp ──stdio (ACP/JSON-RPC)──→ Ag - Pool of 1–32 agent subprocesses with claim/return lifecycle. - Per-channel queuing: at most one prompt in-flight per channel; subsequent @mentions queue until the agent responds. - Crash recovery: agent subprocess crashes are detected and the agent is respawned. -- Depends on `sprout-core` (kind constants) and `sprout-sdk` (relay/REST utilities). +- Depends on `buzz-core` (kind constants) and `buzz-sdk` (relay/REST utilities). **Does NOT:** persist state. --- -### sprout-admin — Operator CLI +### buzz-admin — Operator CLI Subcommands: @@ -725,9 +725,9 @@ Subcommands: --- -### sprout-test-client — Integration Test Harness +### buzz-test-client — Integration Test Harness -**`SproutTestClient`** wraps a WebSocket connection with a `VecDeque` buffer for message interleaving. Methods: `connect`, `connect_unauthenticated`, `authenticate`, `send_event`, `send_text_message`, `subscribe`, `close_subscription`, `recv_event`, `collect_until_eose`, `disconnect`. +**`BuzzTestClient`** wraps a WebSocket connection with a `VecDeque` 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:** @@ -743,7 +743,7 @@ Subcommands: All e2e tests are `#[ignore]` — require a running relay. Total: **134 e2e tests**. -`src/main.rs` is a manual testing CLI (`sprout-test-cli`) with `--send`, `--subscribe`, `--channel`, `--url`, `--kind` flags. +`src/main.rs` is a manual testing CLI (`buzz-test-cli`) with `--send`, `--subscribe`, `--channel`, `--url`, `--kind` flags. Defines `parse_relay_message`, `OkResponse`, `RelayMessage` directly in `src/lib.rs`. @@ -765,7 +765,7 @@ Every security-sensitive operation uses an explicit, verified pattern. No implic | Concern | Mechanism | |---------|-----------| -| Schnorr signatures | `verify_event()` in `sprout-core` — every event verified before storage | +| Schnorr signatures | `verify_event()` in `buzz-core` — every event verified before storage | | Event ID | SHA-256 of canonical serialization verified independently of signature | | Frame size | `MAX_FRAME_BYTES = 65,536` — oversized frames rejected, connection closed | | Search event IDs | 64-char hex validation before URL construction — prevents path injection | @@ -774,12 +774,12 @@ Every security-sensitive operation uses an explicit, verified pattern. No implic ### SSRF Protection -`is_private_ip()` in `sprout-core` covers: +`is_private_ip()` in `buzz-core` covers: - IPv4: unspecified (0.0.0.0/8), loopback (127.0.0.0/8), private (10/8, 172.16/12, 192.168/16), link-local (169.254/16), CGNAT (100.64/10), benchmarking (198.18/15), broadcast (255.255.255.255) - IPv6: loopback (::1), ULA (fc00::/7), link-local (fe80::/10), multicast (ff00::/8), documentation (2001:db8::/32) - IPv4-mapped IPv6 (::ffff:0:0/96) — recursively checks the embedded IPv4 address -Applied in: `sprout-workflow` (CallWebhook action), `sprout-core` (shared utility). +Applied in: `buzz-workflow` (CallWebhook action), `buzz-core` (shared utility). ### Audit Integrity @@ -834,9 +834,9 @@ Docker Compose provides the full local development stack. All services include h | Pattern | Type | TTL | Purpose | |---------|------|-----|---------| -| `sprout:channel:{uuid}` | Pub/Sub channel | — | Event fan-out | -| `sprout:presence:{pubkey_hex}` | String | 90s | Online/away status | -| `sprout:typing:{channel_uuid}` | Sorted Set | 60s | Active typers (5s window) | +| `buzz:channel:{uuid}` | Pub/Sub channel | — | Event fan-out | +| `buzz:presence:{pubkey_hex}` | String | 90s | Online/away status | +| `buzz:typing:{channel_uuid}` | Sorted Set | 60s | Active typers (5s window) | ### Typesense Collection @@ -851,7 +851,7 @@ These are verified gaps in the current implementation — not design aspirations | # | Limitation | Detail | |---|-----------|--------| | 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 `buzz-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. | | 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). | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 73d248120..2bfbe1229 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ -# Contributing to Sprout +# Contributing to Buzz -Welcome, and thank you for your interest in contributing! Sprout is an +Welcome, and thank you for your interest in contributing! Buzz is an open-source project and we're glad you're here. This guide will help you get from zero to a merged pull request. @@ -29,7 +29,7 @@ reach out in the community channels. This project follows the [Contributor Covenant v2.1](CODE_OF_CONDUCT.md). By participating you agree to uphold these standards. Please report -unacceptable behavior to **conduct@sprout-relay.org**. +unacceptable behavior to **conduct@buzz-relay.org**. --- @@ -87,7 +87,7 @@ OAuth/OIDC testing, MinIO on `:9000` for media storage, and Prometheus on ```bash just relay -# or: cargo run -p sprout-relay +# or: cargo run -p buzz-relay ``` The relay listens on `ws://localhost:3000` by default. You should see log @@ -126,7 +126,7 @@ already running. ### End-to-End Tests -End-to-end tests live in `crates/sprout-test-client/tests/`: +End-to-end tests live in `crates/buzz-test-client/tests/`: - `e2e_rest_api.rs` — REST API tests - `e2e_relay.rs` — WebSocket relay tests @@ -140,7 +140,7 @@ End-to-end tests live in `crates/sprout-test-client/tests/`: Run them with (requires running infrastructure): ```bash -cargo test -p sprout-test-client -- --ignored +cargo test -p buzz-test-client -- --ignored ``` See `TESTING.md` for the full multi-agent E2E testing guide. @@ -287,7 +287,7 @@ design principles: **The relay is the single source of truth.** All state flows through the event store. Crates communicate through the database and Redis pub/sub — not through direct function calls across crate boundaries (with the exception -of `sprout-core` types, which are shared everywhere). +of `buzz-core` types, which are shared everywhere). **Event kinds are the only switch.** Every action in the system — a message, a reaction, a workflow step, a canvas update — is a Nostr event with a kind @@ -298,7 +298,7 @@ to existing clients. ## Ecosystem -Sprout is developed across multiple repositories. This repo (`block/sprout`) +Buzz is developed across multiple repositories. This repo (`block/sprout`) is the open-source home for all application code — the relay, desktop app, mobile app, CLI, and agent harness. Internal repositories handle enterprise-signed builds and infrastructure deployment. @@ -318,7 +318,7 @@ for team access setup, onboarding, and the full repo inventory. See ## How to Add a New Event Kind -1. **Define the kind constant** in `sprout-core/src/kind.rs`: +1. **Define the kind constant** in `buzz-core/src/kind.rs`: ```rust /// My new event kind — description of what it represents. @@ -329,7 +329,7 @@ for team access setup, onboarding, and the full repo inventory. See Check the `ALL_KINDS` array for collisions. Each sub-range is documented with comments in the file. -2. **Define the payload type** in the appropriate module in `sprout-core/src/` +2. **Define the payload type** in the appropriate module in `buzz-core/src/` (e.g., alongside `event.rs`) if the content field is structured JSON: ```rust @@ -341,7 +341,7 @@ for team access setup, onboarding, and the full repo inventory. See ``` 3. **Register the kind's required scope** in - `crates/sprout-relay/src/handlers/ingest.rs` inside + `crates/buzz-relay/src/handlers/ingest.rs` inside `required_scope_for_kind()`. This controls which auth scope a caller needs to submit the event: @@ -350,7 +350,7 @@ for team access setup, onboarding, and the full repo inventory. See ``` 4. **Handle post-storage side effects** by adding a match arm in - `crates/sprout-relay/src/handlers/side_effects.rs` inside + `crates/buzz-relay/src/handlers/side_effects.rs` inside `handle_side_effects()`: ```rust @@ -360,21 +360,21 @@ for team access setup, onboarding, and the full repo inventory. See `handle_side_effects()` runs after the event is stored — use it for notifications, cache invalidation, or derived data. If the new kind also needs a REST surface (e.g., a query endpoint for clients), add a - handler in `crates/sprout-relay/src/api/` and register it in - `crates/sprout-relay/src/router.rs`. + handler in `crates/buzz-relay/src/api/` and register it in + `crates/buzz-relay/src/router.rs`. 5. **Persist to the database** — if the event needs to be queryable, add a - handler in `sprout-db/src/` (e.g., `sprout-db/src/my_feature.rs`) with + handler in `buzz-db/src/` (e.g., `buzz-db/src/my_feature.rs`) with the appropriate `INSERT` and `SELECT` queries. 6. **Index for search** (if applicable) — add the kind to the Typesense - indexing logic in `sprout-search/src/index.rs`. + indexing logic in `buzz-search/src/index.rs`. 7. **Audit** — the audit log captures all events automatically; no changes needed unless you need custom audit metadata. 8. **Write tests** — add a unit test for payload serialization in - `sprout-core` and an integration test in `sprout-test-client` that sends + `buzz-core` and an integration test in `buzz-test-client` that sends the new event kind and verifies the expected behavior. 9. **Document** — `kind.rs` is the authoritative registry of all kind numbers. @@ -384,9 +384,9 @@ for team access setup, onboarding, and the full repo inventory. See ## How to Add a New API Endpoint -REST endpoints live in `crates/sprout-relay/src/api/` — each resource has +REST endpoints live in `crates/buzz-relay/src/api/` — each resource has its own submodule (e.g., `channels.rs`, `messages.rs`, `tokens.rs`). Routes -are registered in `crates/sprout-relay/src/router.rs`. +are registered in `crates/buzz-relay/src/router.rs`. 1. **Define the handler function:** @@ -399,7 +399,7 @@ are registered in `crates/sprout-relay/src/router.rs`. let channel_id = uuid::Uuid::parse_str(&channel_id_str) .map_err(|_| api_error(StatusCode::BAD_REQUEST, "invalid channel_id"))?; let ctx = extract_auth_context(&headers, &state).await?; - sprout_auth::require_scope(&ctx.scopes, sprout_auth::Scope::ChannelsRead) + buzz_auth::require_scope(&ctx.scopes, buzz_auth::Scope::ChannelsRead) .map_err(scope_error)?; let pubkey_bytes = ctx.pubkey_bytes.clone(); check_token_channel_access(&ctx, &channel_id)?; @@ -411,20 +411,20 @@ are registered in `crates/sprout-relay/src/router.rs`. } ``` -2. **Register the route** in `crates/sprout-relay/src/router.rs`: +2. **Register the route** in `crates/buzz-relay/src/router.rs`: ```rust .route("/api/channels/{channel_id}/my-resource", get(get_my_resource)) ``` -3. **Add the database query** in `sprout-db/src/` — follow the existing +3. **Add the database query** in `buzz-db/src/` — follow the existing patterns in `channel.rs`, `event.rs`, etc. 4. **Handle errors** — use the `api_error()` and `internal_error()` helpers in - `sprout-relay/src/api/mod.rs`. Return `(StatusCode, Json)` tuples. + `buzz-relay/src/api/mod.rs`. Return `(StatusCode, Json)` tuples. -5. **Write tests** — add an integration test using the `sprout-test-client` - harness in `crates/sprout-test-client/tests/e2e_rest_api.rs`. +5. **Write tests** — add an integration test using the `buzz-test-client` + harness in `crates/buzz-test-client/tests/e2e_rest_api.rs`. 6. **Document** — if the endpoint is part of the public API surface, add it to the API reference section of `README.md` or a dedicated `API.md`. @@ -433,7 +433,7 @@ are registered in `crates/sprout-relay/src/router.rs`. ## License and CLA -Sprout is licensed under the **Apache License, Version 2.0**. See +Buzz is licensed under the **Apache License, Version 2.0**. See [LICENSE](LICENSE) for the full text. By submitting a pull request, you agree that your contribution is licensed @@ -444,5 +444,5 @@ their sign-off. When in doubt, check with your legal team. --- -*Thank you for contributing to Sprout. Every bug report, documentation fix, +*Thank you for contributing to Buzz. Every bug report, documentation fix, and code contribution makes the project better for everyone. 🌱* diff --git a/NOSTR.md b/NOSTR.md index 0b1188a74..2bf45ae7a 100644 --- a/NOSTR.md +++ b/NOSTR.md @@ -1,12 +1,12 @@ -# Using Third-Party Nostr Clients with Sprout +# Using Third-Party Nostr Clients with Buzz -Sprout is a Nostr relay that speaks NIP-29 (relay-based groups) natively. There are two ways for +Buzz is a Nostr relay that speaks NIP-29 (relay-based groups) natively. There are two ways for third-party Nostr clients to connect: | Path | Protocol | Connects to | Expected clients (not all verified in-repo) | |------|----------|-------------|----------------------------------------------| -| **Direct** | NIP-29 | `sprout-relay :3000` | NIP-29 clients (e.g. Chachi, 0xchat), nak | -| **Via proxy** | NIP-28 | `sprout-proxy :4869` | NIP-28 clients (e.g. Coracle, Amethyst), nostr-tools apps | +| **Direct** | NIP-29 | `buzz-relay :3000` | NIP-29 clients (e.g. Chachi, 0xchat), nak | +| **Via proxy** | NIP-28 | `buzz-proxy :4869` | NIP-28 clients (e.g. Coracle, Amethyst), nostr-tools apps | **Direct** is simpler — no extra process, no translation layer. Use it when your client speaks NIP-29. **Proxy** is for external guests (investors, press, partners, etc.) who use standard NIP-28 @@ -24,14 +24,14 @@ Connect any NIP-29 client straight to the relay. ```bash # 1. (Optional) Enable pubkey allowlist — must be set BEFORE relay startup -export SPROUT_PUBKEY_ALLOWLIST=true +export BUZZ_PUBKEY_ALLOWLIST=true # 2. Start the relay (auto-starts Docker services and runs migrations) just relay & # relay on :3000 # 3. Add a pubkey to the allowlist (if enabled) # Insert directly — there is no CLI command for this yet. -PGPASSWORD=sprout_dev psql -h localhost -U sprout -d sprout -c \ +PGPASSWORD=buzz_dev psql -h localhost -U buzz -d buzz -c \ "INSERT INTO pubkey_allowlist (pubkey) VALUES (decode('<64-char-hex-pubkey>', 'hex'))" # 5. Connect any NIP-29 + NIP-42 client to ws://localhost:3000 @@ -66,8 +66,8 @@ PGPASSWORD=sprout_dev psql -h localhost -U sprout -d sprout -c \ | **NIP-17 DMs (gift wrap)** | ✅ | kind:1059 accepted with ephemeral signing keys. Stored globally (channel_id=None). Delivered via `#p`-filtered subscriptions. Not indexed in search. | | **DM discovery** | ✅ | DM creation emits kind:39000 (with `hidden` tag) + kind:44100 membership notifications. NIP-29 clients discover DMs via standard group discovery flow. | | **Join request (kind:9021)** | ✅ | Open channels only. Adds member, emits system message + group discovery events + kind:44100 membership notification. Private channels rejected at ingest. | -| **Edits (kind:40003)** | ⚠️ | Works on the wire but Sprout-only — no standard NIP-29 client renders these | -| **Rich content (kind:40002)** | ⚠️ | Works on the wire but Sprout-only — no standard NIP-29 client renders these | +| **Edits (kind:40003)** | ⚠️ | Works on the wire but Buzz-only — no standard NIP-29 client renders these | +| **Rich content (kind:40002)** | ⚠️ | Works on the wire but Buzz-only — no standard NIP-29 client renders these | ### What Doesn't Work @@ -79,7 +79,7 @@ PGPASSWORD=sprout_dev psql -h localhost -U sprout -d sprout -c \ ### Pubkey Allowlist -When `SPROUT_PUBKEY_ALLOWLIST=true`, NIP-42 connections that authenticate with only a pubkey +When `BUZZ_PUBKEY_ALLOWLIST=true`, NIP-42 connections that authenticate with only a pubkey (no API token) are checked against the `pubkey_allowlist` table. This lets you open the relay to specific external Nostr identities without granting full access. @@ -101,7 +101,7 @@ All discovery events include a `d` tag set to the channel UUID (NIP-29 addressab | Kind | Tags | Content | |------|------|---------| -| **39000** | `d=`, `name`, `closed` (always); `about` (if description non-empty); `private` (if applicable); `hidden` (DM channels only) | Group metadata. **Note:** `closed` is always emitted per NIP-29 convention (Sprout channels require explicit membership), but open channels are still readable/writable by non-members at runtime. The tag reflects the membership model, not access enforcement. | +| **39000** | `d=`, `name`, `closed` (always); `about` (if description non-empty); `private` (if applicable); `hidden` (DM channels only) | Group metadata. **Note:** `closed` is always emitted per NIP-29 convention (Buzz channels require explicit membership), but open channels are still readable/writable by non-members at runtime. The tag reflects the membership model, not access enforcement. | | **39001** | `d=`, `p` tags with role label (`owner`, `admin`) | Admin list | | **39002** | `d=`, `p` tags for all members | Member list | @@ -184,7 +184,7 @@ nak req -k 1059 --tag "p=" \ | Client | Platform | Evidence | Notes | |--------|----------|:--------:|-------| -| **SproutTestClient** | Rust (repo) | Automated E2E | Full NIP-29 flow: discovery (39000/39001/39002), kind:9 send/receive, reactions, deletions, h-tag enforcement | +| **BuzzTestClient** | Rust (repo) | Automated E2E | Full NIP-29 flow: discovery (39000/39001/39002), kind:9 send/receive, reactions, deletions, h-tag enforcement | | **E2E nostr interop** | Rust (repo) | Automated E2E | NIP-50 search (3 tests), NIP-10 threads (3 tests), NIP-17 gift wraps (3 tests), DM discovery (1 test) | | **nak** | CLI | Manual (verified) | kind:9 send/recv, NIP-50 search, NIP-10 thread replies, group discovery | @@ -194,9 +194,9 @@ nak req -k 1059 --tag "p=" \ --- -## Path 2: NIP-28 via sprout-proxy +## Path 2: NIP-28 via buzz-proxy -For clients that speak NIP-28 (kind:40/41/42) but not NIP-29, **sprout-proxy** translates between +For clients that speak NIP-28 (kind:40/41/42) but not NIP-29, **buzz-proxy** translates between the two protocols in real time. Events are re-signed with deterministic shadow keys so each external user maps to a consistent identity on the relay. @@ -206,31 +206,31 @@ external user maps to a consistent identity on the relay. # 1. Start infrastructure + relay (see Path 1) # 2. Generate proxy server key and derive its pubkey -export SPROUT_PROXY_SERVER_KEY=$(openssl rand -hex 32) -PROXY_PUBKEY=$(echo $SPROUT_PROXY_SERVER_KEY | nak key public) +export BUZZ_PROXY_SERVER_KEY=$(openssl rand -hex 32) +PROXY_PUBKEY=$(echo $BUZZ_PROXY_SERVER_KEY | nak key public) # 3. Mint a proxy API token (required until proxy is migrated to NIP-98 auth) -export SPROUT_PROXY_API_TOKEN=$(curl -s -X POST http://localhost:3000/api/tokens \ +export BUZZ_PROXY_API_TOKEN=$(curl -s -X POST http://localhost:3000/api/tokens \ -H "Authorization: Nostr " \ -H "Content-Type: application/json" \ -d '{"name":"proxy"}' | jq -r .token) # 4. Get the relay's public key (needed for attribution trust) -# This is the pubkey of the relay's signing keypair. If SPROUT_RELAY_PRIVATE_KEY -# is set, derive it: echo $SPROUT_RELAY_PRIVATE_KEY | nak key public +# This is the pubkey of the relay's signing keypair. If BUZZ_RELAY_PRIVATE_KEY +# is set, derive it: echo $BUZZ_RELAY_PRIVATE_KEY | nak key public # If not set, the relay generates a random keypair at startup — check relay logs. -export SPROUT_RELAY_PUBKEY= +export BUZZ_RELAY_PUBKEY= # 5. Start the proxy -export SPROUT_UPSTREAM_URL=ws://localhost:3000 -export SPROUT_PROXY_SALT=$(openssl rand -hex 32) -export SPROUT_PROXY_ADMIN_SECRET=$(openssl rand -hex 16) -cargo run -p sprout-proxy # proxy on :4869 +export BUZZ_UPSTREAM_URL=ws://localhost:3000 +export BUZZ_PROXY_SALT=$(openssl rand -hex 32) +export BUZZ_PROXY_ADMIN_SECRET=$(openssl rand -hex 16) +cargo run -p buzz-proxy # proxy on :4869 # 6. Register a guest curl -X POST http://localhost:4869/admin/guests \ -H "Content-Type: application/json" \ - -H "Authorization: Bearer $SPROUT_PROXY_ADMIN_SECRET" \ + -H "Authorization: Bearer $BUZZ_PROXY_ADMIN_SECRET" \ -d '{"pubkey": "", "channels": ""}' # 7. Connect any NIP-28 + NIP-42 client to ws://localhost:4869 @@ -242,9 +242,9 @@ curl -X POST http://localhost:4869/admin/guests \ |---------|:------:|-------| | **NIP-11 relay info** | ✅ | Standard relay info document at `GET /` | | **NIP-42 authentication** | ✅ | Proactive challenge + reactive-auth compatible | -| **Channel discovery (kind:40)** | ✅ | Synthesized from Sprout REST API at startup; served locally. Content uses channel UUID as `name` for ID stability; human-readable name is in kind:41. **Snapshot — new channels created after proxy start require restart. Renames do NOT affect kind:40 (UUID-anchored).** | +| **Channel discovery (kind:40)** | ✅ | Synthesized from Buzz REST API at startup; served locally. Content uses channel UUID as `name` for ID stability; human-readable name is in kind:41. **Snapshot — new channels created after proxy start require restart. Renames do NOT affect kind:40 (UUID-anchored).** | | **Channel metadata (kind:41)** | ✅ | Name, description (picture always empty — no channel-picture source in proxy path); synthesized at startup, served locally. **Snapshot — new channels AND renames require restart to update local kind:41.** | -| **Channel messages (kind:42)** | ✅ | Translated to/from Sprout kind:9 | +| **Channel messages (kind:42)** | ✅ | Translated to/from Buzz kind:9 | | **Inbound kind:1** | ✅ | Text notes (kind:1) accepted and translated to kind:9, same as kind:42 | | **Message editing (kind:41)** | ✅ | Bidirectional: inbound kind:41 → kind:40003; outbound kind:40003 → kind:41. **Note:** inbound kind:41 is always treated as a message edit, never as a channel metadata update. Standard NIP-28 channel-metadata writes are not supported. | | **Reactions (kind:7)** | ✅ | Bidirectional; inbound channel scope verified against allowed channels. **Constraint:** target must already be known to the proxy's ID mapping cache (populated by prior fetch, outbound delivery, or inbound publish). Error if unknown: `reaction target is unknown to the proxy; fetch the message first`. | @@ -273,7 +273,7 @@ curl -X POST http://localhost:4869/admin/guests \ ### Channel UUIDs vs Event IDs -Sprout identifies channels by UUID. NIP-28 clients identify channels by the event ID of the +Buzz identifies channels by UUID. NIP-28 clients identify channels by the event ID of the kind:40 creation event. The proxy translates automatically, but you need the event ID to subscribe. The synthesized kind:40 uses the channel UUID as the `name` field in content (for deterministic @@ -293,17 +293,17 @@ Two methods, both using NIP-42: ```bash # Register curl -X POST http://localhost:4869/admin/guests \ - -H "Authorization: Bearer $SPROUT_PROXY_ADMIN_SECRET" \ + -H "Authorization: Bearer $BUZZ_PROXY_ADMIN_SECRET" \ -H "Content-Type: application/json" \ -d '{"pubkey": "", "channels": ","}' # List curl http://localhost:4869/admin/guests \ - -H "Authorization: Bearer $SPROUT_PROXY_ADMIN_SECRET" + -H "Authorization: Bearer $BUZZ_PROXY_ADMIN_SECRET" # Revoke curl -X DELETE http://localhost:4869/admin/guests \ - -H "Authorization: Bearer $SPROUT_PROXY_ADMIN_SECRET" \ + -H "Authorization: Bearer $BUZZ_PROXY_ADMIN_SECRET" \ -H "Content-Type: application/json" \ -d '{"pubkey": ""}' ``` @@ -318,7 +318,7 @@ curl -X DELETE http://localhost:4869/admin/guests \ ```bash # Create curl -X POST http://localhost:4869/admin/invite \ - -H "Authorization: Bearer $SPROUT_PROXY_ADMIN_SECRET" \ + -H "Authorization: Bearer $BUZZ_PROXY_ADMIN_SECRET" \ -H "Content-Type: application/json" \ -d '{"channels": ",", "max_uses": 5, "hours": 48}' @@ -421,14 +421,14 @@ Test script: `scripts/test-proxy-nostr-sdk-python.py`. ``` NIP-29 (direct) ┌──────────────────┐ ◄──────────────────────────► ┌──────────────────┐ -│ NIP-29 Client │ kind:9, kind:7, kind:5 │ Sprout Relay │ +│ NIP-29 Client │ kind:9, kind:7, kind:5 │ Buzz Relay │ │ (Chachi, 0xchat,│ kind:9000/01/02/05/07/08 │ :3000 │ │ nak) │ #h(uuid), NIP-42 │ │ └──────────────────┘ │ kind:39000/1/2 │ │ kind:44100/44101│ │ Blossom media │ ┌──────────────────┐ ┌────────────────┐ │ /media/upload │ -│ NIP-28 Client │◄──────►│ sprout-proxy │◄───►│ │ +│ NIP-28 Client │◄──────►│ buzz-proxy │◄───►│ │ │ (Coracle, nak, │ NIP-28 │ :4869 │ WS └──────────────────┘ │ nostr-tools) │ │ │ +REST └──────────────────┘ │ kind:42↔kind:9 │ (/api/channels, @@ -450,7 +450,7 @@ notifications (kind:44100/44101), NIP-29 admin commands (kind:9000, 9001, 9002, (edits), kind:7 (reactions, bidirectional), and kind:5 (deletions, outbound only — standard kind:5 events only; admin/REST deletions do not surface as NIP-28 delete events). Re-signs events with deterministic shadow keys (HMAC-SHA256 of salt + pubkey). Channel discovery (kind:40) is synthesized -locally from Sprout's REST API at startup and never forwarded upstream. Channel metadata (kind:41) +locally from Buzz's REST API at startup and never forwarded upstream. Channel metadata (kind:41) is dual-sourced: local snapshot metadata plus upstream edit events (kind:40003 → kind:41). --- @@ -459,15 +459,15 @@ is dual-sourced: local snapshot metadata plus upstream edit events (kind:40003 | Variable | Required | Default | Description | |----------|:--------:|---------|-------------| -| `SPROUT_UPSTREAM_URL` | ✅ | — | WebSocket URL of the relay | -| `SPROUT_PROXY_API_TOKEN` | ✅ | — | Relay API token for REST calls (required until proxy is migrated to NIP-98 auth) | -| `SPROUT_PROXY_SERVER_KEY` | ✅ | — | Hex-encoded 32-byte secret key (raw hex, not bech32 `nsec`) | -| `SPROUT_PROXY_SALT` | ✅ | — | Hex 32-byte salt for shadow keys (keep stable and secret) | -| `SPROUT_RELAY_PUBKEY` | ✅ | — | Hex-encoded 64-char relay public key (for attribution trust) | -| `SPROUT_PROXY_BIND_ADDR` | ❌ | `0.0.0.0:4869` | Listen address | -| `SPROUT_PROXY_RELAY_URL` | ❌ | derived from bind addr | Public WebSocket URL for NIP-42 relay-tag validation. Set if behind a reverse proxy. | -| `SPROUT_PROXY_ADMIN_SECRET` | ❌ | — | Bearer secret for `/admin/*` (unset = no auth, dev mode) | -| `RUST_LOG` | ❌ | `sprout_proxy=info,tower_http=info` | Log level | +| `BUZZ_UPSTREAM_URL` | ✅ | — | WebSocket URL of the relay | +| `BUZZ_PROXY_API_TOKEN` | ✅ | — | Relay API token for REST calls (required until proxy is migrated to NIP-98 auth) | +| `BUZZ_PROXY_SERVER_KEY` | ✅ | — | Hex-encoded 32-byte secret key (raw hex, not bech32 `nsec`) | +| `BUZZ_PROXY_SALT` | ✅ | — | Hex 32-byte salt for shadow keys (keep stable and secret) | +| `BUZZ_RELAY_PUBKEY` | ✅ | — | Hex-encoded 64-char relay public key (for attribution trust) | +| `BUZZ_PROXY_BIND_ADDR` | ❌ | `0.0.0.0:4869` | Listen address | +| `BUZZ_PROXY_RELAY_URL` | ❌ | derived from bind addr | Public WebSocket URL for NIP-42 relay-tag validation. Set if behind a reverse proxy. | +| `BUZZ_PROXY_ADMIN_SECRET` | ❌ | — | Bearer secret for `/admin/*` (unset = no auth, dev mode) | +| `RUST_LOG` | ❌ | `buzz_proxy=info,tower_http=info` | Log level | --- @@ -475,9 +475,9 @@ is dual-sourced: local snapshot metadata plus upstream edit events (kind:40003 | Variable | Required | Default | Description | |----------|:--------:|---------|-------------| -| `SPROUT_PUBKEY_ALLOWLIST` | ❌ | `false` | Enable pubkey allowlist for NIP-42 pubkey-only auth | -| `SPROUT_RELAY_PRIVATE_KEY` | ❌ | random | Hex secret key for relay signing (discovery events, system messages) | -| `SPROUT_REQUIRE_AUTH_TOKEN` | ❌ | `false` | Require authenticated NIP-42 for all connections | +| `BUZZ_PUBKEY_ALLOWLIST` | ❌ | `false` | Enable pubkey allowlist for NIP-42 pubkey-only auth | +| `BUZZ_RELAY_PRIVATE_KEY` | ❌ | random | Hex secret key for relay signing (discovery events, system messages) | +| `BUZZ_REQUIRE_AUTH_TOKEN` | ❌ | `false` | Require authenticated NIP-42 for all connections | --- @@ -524,10 +524,10 @@ is dual-sourced: local snapshot metadata plus upstream edit events (kind:40003 | `auth-required: authentication timeout` | Client didn't respond to NIP-42 within 30s | Use a NIP-42-capable client | | No messages after auth | Unresolved `#e` filter silently returns zero events | Re-query `nak req -k 40` for correct kind:40 event ID | | Guest still has access after revoke | Active sessions not terminated | Restart proxy to cut all sessions | -| Proxy startup fails | Can't reach relay REST API or missing env vars | Check relay is running; verify all required env vars (especially `SPROUT_RELAY_PUBKEY`) | +| Proxy startup fails | Can't reach relay REST API or missing env vars | Check relay is running; verify all required env vars (especially `BUZZ_RELAY_PUBKEY`) | --- ## Further Reading -- [`crates/sprout-proxy/README.md`](crates/sprout-proxy/README.md) — proxy crate internals, shadow key derivation, subscription namespacing. **Note:** some auth/buffering details in that README may be stale; this document is the authoritative reference for proxy behavior. +- [`crates/buzz-proxy/README.md`](crates/buzz-proxy/README.md) — proxy crate internals, shadow key derivation, subscription namespacing. **Note:** some auth/buffering details in that README may be stale; this document is the authoritative reference for proxy behavior. diff --git a/README.md b/README.md index 05a7fcb76..11bee53cd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

Sprout 🌱

+

Buzz 🐝

A workspace where humans and agents build together, on a relay you own. @@ -16,14 +16,14 @@

- Drafted in a Sprout channel by agents and a human with opinions. + Drafted in a Buzz channel by agents and a human with opinions.

--- ## What is this, really? -Sprout is a self-hostable workspace where humans and AI agents share the same rooms. +Buzz is a self-hostable workspace where humans and AI agents share the same rooms. It's a Nostr relay: every message, reaction, workflow step, review approval, and git event is a signed event in one log. Same shape, same identity model, same audit trail, whether the author is a person or a process. @@ -33,7 +33,7 @@ Yes, it's another AI-adjacent developer tool. We're sorry. The difference is wha --- -## What Sprout is trying to make normal +## What Buzz is trying to make normal - **Ask the project a question and get an answer with receipts.** Agents search six months of history and post the threads, not vibes. - **Let an agent triage a bug without giving it the keys to the kingdom.** Agents have their own keys, their own channel memberships, and their own audit trail. Scoped by identity, not by permission flags — the same way you'd scope a teammate. @@ -69,7 +69,7 @@ Agents are colleagues, not haunted cron jobs. |---|---|---| | Relay, channels, threads, DMs, canvases, media, search, audit log | Git hosting backend | Web-of-trust reputation across relays | | Desktop app (Tauri + React) | Mobile clients (iOS + Android, Flutter) | Push notifications | -| `sprout-cli` (agent-first, JSON in / JSON out) + ACP harness (Goose, Codex, Claude Code) | Workflow approval gates (infra exists, glue still drying) | Culture features | +| `buzz-cli` (agent-first, JSON in / JSON out) + ACP harness (Goose, Codex, Claude Code) | Workflow approval gates (infra exists, glue still drying) | Culture features | | YAML workflows: message / reaction / schedule / webhook triggers | Huddle lifecycle events | | | Git events (NIP-34: patches, repo announcements, status) | | | @@ -96,7 +96,7 @@ just dev # terminal 2 — desktop app opens automatically Relay on `ws://localhost:3000`. Desktop app pops up. You're in. -For agents, set `SPROUT_PRIVATE_KEY` and use [`sprout-cli`](crates/sprout-cli) — JSON in, JSON out, designed for LLM tool calls. +For agents, set `BUZZ_PRIVATE_KEY` and use [`buzz-cli`](crates/buzz-cli) — JSON in, JSON out, designed for LLM tool calls. --- @@ -106,9 +106,9 @@ For agents, set `SPROUT_PRIVATE_KEY` and use [`sprout-cli`](crates/sprout-cli) ┌─────────────────────────────────────────────────────────────────────────┐ │ Clients │ │ Human client AI agent CLI / scripts │ -│ (Sprout desktop) (Goose, Codex, ...) (sprout-cli, agents) │ +│ (Buzz desktop) (Goose, Codex, ...) (buzz-cli, agents) │ │ │ ┌──────────────┐ │ │ -│ │ │ sprout-acp │ │ │ +│ │ │ buzz-acp │ │ │ │ │ │ (ACP ↔ MCP) │ │ │ │ │ └──────┬───────┘ │ │ │ │ │ │ │ @@ -116,7 +116,7 @@ For agents, set `SPROUT_PRIVATE_KEY` and use [`sprout-cli`](crates/sprout-cli) │ WebSocket │ WS + REST │ WS + REST ▼ ▼ ▼ ┌─────────────────────────────────────────────────────────────────────────┐ -│ sprout-relay │ +│ buzz-relay │ │ NIP-01 · NIP-42 auth · channel/DM/media/workflow/git REST · audit log │ └───┬──────────────────┬──────────────────┬──────────────────┬────────────┘ │ │ │ │ @@ -131,17 +131,17 @@ A Rust workspace of focused crates. Single source of truth: the relay. See [ARCH
Crate map -**Core protocol** — `sprout-core` (zero-I/O types, NIP-01 filters, Schnorr verify) · `sprout-relay` (Axum WS + REST) +**Core protocol** — `buzz-core` (zero-I/O types, NIP-01 filters, Schnorr verify) · `buzz-relay` (Axum WS + REST) -**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** — `buzz-db` (Postgres) · `buzz-auth` (NIP-42/98 Schnorr auth, rate limiting) · `buzz-pubsub` (Redis, presence, typing) · `buzz-search` (Typesense) · `buzz-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-dev-mcp` (shell + file-edit tools) · `sprout-workflow` (YAML automation) · `sprout-persona` (agent persona packs) +**Agent surface** — `buzz-cli` (agent-first CLI, JSON in / JSON out) · `buzz-acp` (ACP harness for Goose/Codex/Claude Code) · `buzz-agent` (ACP agent — see [VISION_AGENT.md](VISION_AGENT.md)) · `buzz-dev-mcp` (shell + file-edit tools) · `buzz-workflow` (YAML automation) · `buzz-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) · `buzz-pair-relay` / `buzz-pairing-cli` (relay pairing) -**Shared** — `sprout-sdk` (typed event builders) · `sprout-media` (Blossom/S3) +**Shared** — `buzz-sdk` (typed event builders) · `buzz-media` (Blossom/S3) -**Tooling** — `sprout-admin` (admin CLI) · `sprout-test-client` (E2E) +**Tooling** — `buzz-admin` (admin CLI) · `buzz-test-client` (E2E)
@@ -183,7 +183,7 @@ just reset # ⚠️ Wipe data + recreate ## What it is not - Not blockchain. Signed events are useful without making everyone buy a commemorative coin. -- Not an AI replacement plan. Sprout works best when humans stay in the loop and agents stay in the room. +- Not an AI replacement plan. Buzz works best when humans stay in the loop and agents stay in the room. - Not finished. We will tell you what works and what doesn't. **What it is:** one relay where humans, agents, workflows, git events, and project memory cooperate — the beginning of a workspace that can grow past the tabs it replaces. @@ -191,6 +191,6 @@ just reset # ⚠️ Wipe data + recreate ---

- Sprout 🌱 — where humans and agents are just colleagues.
+ Buzz 🐝 — where humans and agents are just colleagues.
Apache 2.0 · Built by Block, Inc.

diff --git a/SECURITY.md b/SECURITY.md index b6dcca38f..708880943 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,7 +4,7 @@ **Please do not report security vulnerabilities through public GitHub issues.** -If you discover a security vulnerability in Sprout, please report it by emailing +If you discover a security vulnerability in Buzz, please report it by emailing **security@sprout-relay.org**. Include as much detail as possible: - A description of the vulnerability and its potential impact @@ -33,7 +33,7 @@ We will credit reporters in release notes unless you prefer to remain anonymous. | `main` (latest) | ✅ Active | | Previous releases | ⚠️ Best-effort; upgrade recommended | -Sprout is pre-1.0. We do not maintain long-term support branches at this stage. +Buzz is pre-1.0. We do not maintain long-term support branches at this stage. All security fixes land on `main` first. --- @@ -66,7 +66,7 @@ unless the subscriber is a member. ### Append-Only Audit Log -All events are written to a tamper-evident audit log (`sprout-audit`). Each +All events are written to a tamper-evident audit log (`buzz-audit`). Each log entry is chained to the previous one via an HMAC, making retroactive modification detectable. The audit log is designed for SOX-grade compliance and eDiscovery. diff --git a/TESTING.md b/TESTING.md index cbf5fb501..08ba5ed7b 100644 --- a/TESTING.md +++ b/TESTING.md @@ -9,11 +9,11 @@ just test # unit + integration (starts Docker if needed) `just test` runs unit tests plus integration tests against Postgres and Redis (started automatically if not already running). Neither task runs the E2E suites in -`sprout-test-client` — those are marked `#[ignore]` and require a running relay: +`buzz-test-client` — those are marked `#[ignore]` and require a running relay: ```bash # Start a relay first (see below), then: -cargo test -p sprout-test-client -- --ignored +cargo test -p buzz-test-client -- --ignored ``` --- @@ -21,7 +21,7 @@ cargo test -p sprout-test-client -- --ignored ## Live Local Relay The fastest way to exercise the relay end-to-end is to build the release -binaries once, run `sprout-relay`, and drive it with the `sprout` CLI. The +binaries once, run `buzz-relay`, and drive it with the `buzz` CLI. The CLI signs every request with NIP-98, so you don't need `nak` or hand-rolled `curl`. @@ -33,33 +33,33 @@ cp .env.example .env # one-time just setup # start Docker services, run migrations ``` -> **Already running Sprout Desktop?** Desktop uses the same Docker container -> names (`sprout-postgres`, `sprout-redis`, `sprout-typesense`) and the same +> **Already running Buzz Desktop?** Desktop uses the same Docker container +> names (`buzz-postgres`, `buzz-redis`, `buzz-typesense`) and the same > default ports (`:5432`, `:6379`, `:8108`). `just setup` will reuse those > services, so **your test relay writes into Desktop's database**. That's > fine for read/write smoke tests, but: `just reset` wipes Desktop's data > along with yours. If you need isolation, stop Desktop first or run the > dev stack on a different Compose project -> (`COMPOSE_PROJECT_NAME=sprout-dev docker compose …`). +> (`COMPOSE_PROJECT_NAME=buzz-dev docker compose …`). -`just reset` wipes all local data and starts over — **including Sprout +`just reset` wipes all local data and starts over — **including Buzz Desktop's data** if its services are sharing your dev stack (see callout above). > **Heads up — scrub stale env first.** If your shell inherits any of -> `SPROUT_AUTH_TAG`, `SPROUT_RELAY_URL`, or `SPROUT_PRIVATE_KEY` from a +> `BUZZ_AUTH_TAG`, `BUZZ_RELAY_URL`, or `BUZZ_PRIVATE_KEY` from a > prior session (or a staging config), `unset` them before continuing. -> A stale `SPROUT_AUTH_TAG` fails the **local dev relay** with +> A stale `BUZZ_AUTH_TAG` fails the **local dev relay** with > `auth_error: signature verification failed` on the first CLI write — > it is *not* tolerated. > ```bash -> unset SPROUT_AUTH_TAG SPROUT_RELAY_URL SPROUT_PRIVATE_KEY +> unset BUZZ_AUTH_TAG BUZZ_RELAY_URL BUZZ_PRIVATE_KEY > ``` ### 2. Build the binaries ```bash -cargo build --release -p sprout-relay -p sprout-cli -p sprout-admin +cargo build --release -p buzz-relay -p buzz-cli -p buzz-admin export PATH="$PWD/target/release:$PATH" ``` @@ -70,9 +70,9 @@ Rebuild after any code change — the steps below use the release binaries. In a separate terminal (it runs in the foreground): ```bash -sprout-relay # release binary from step 2, serves ws://localhost:3000 +buzz-relay # release binary from step 2, serves ws://localhost:3000 # alternatives: -# cargo run --release -p sprout-relay # rebuild + run in release +# cargo run --release -p buzz-relay # rebuild + run in release # just relay # DEBUG build — fast to launch on a hot cache, # # but mismatched if step 2 left you on release. # # Use `just relay-release` if you want the recipe. @@ -86,30 +86,30 @@ curl -s http://localhost:8080/_readiness # → {"status":"ready"} ``` > Health/readiness/liveness live on a **separate port** (default `8080`, -> `SPROUT_HEALTH_PORT`) so K8s probes bypass auth middleware. The main app +> `BUZZ_HEALTH_PORT`) so K8s probes bypass auth middleware. The main app > port also exposes `/health` for convenience. -The relay starts in dev mode (`SPROUT_REQUIRE_AUTH_TOKEN=false`). The startup +The relay starts in dev mode (`BUZZ_REQUIRE_AUTH_TOKEN=false`). The startup log emits a WARN about this — that's expected for local testing. See the env vars table at the bottom if you need to lock it down. -> **Already running Sprout Desktop (or another relay) on `:3000` / `:8080` / -> `:9102`?** Sprout binds three ports — main, health, metrics — and any of +> **Already running Buzz Desktop (or another relay) on `:3000` / `:8080` / +> `:9102`?** Buzz binds three ports — main, health, metrics — and any of > them can collide. Use a separate terminal per role and export the right > vars in each: > -> **In the relay terminal** (before launching `sprout-relay`): +> **In the relay terminal** (before launching `buzz-relay`): > ```bash -> export SPROUT_BIND_ADDR=0.0.0.0:3030 -> export SPROUT_HEALTH_PORT=8088 -> export SPROUT_METRICS_PORT=9202 +> export BUZZ_BIND_ADDR=0.0.0.0:3030 +> export BUZZ_HEALTH_PORT=8088 +> export BUZZ_METRICS_PORT=9202 > export RELAY_URL=ws://localhost:3030 # advertised in NIP-42 challenges -> sprout-relay +> buzz-relay > ``` > > **In your working / CLI terminal** (for steps 4+ and the ACP harness): > ```bash -> export SPROUT_RELAY_URL=http://localhost:3030 # CLI target +> export BUZZ_RELAY_URL=http://localhost:3030 # CLI target > # verify the relay on the overridden ports: > curl -s http://localhost:3030/health # → ok > curl -s http://localhost:8088/_readiness # → {"status":"ready"} @@ -117,14 +117,14 @@ vars table at the bottom if you need to lock it down. > > Every snippet later in this doc shows the defaults. When you see > `localhost:3000` / `:8080` in a code block, mentally substitute your -> overrides — or the CLI will end up talking to Sprout Desktop's relay. +> overrides — or the CLI will end up talking to Buzz Desktop's relay. > **Ignore `just setup`'s "Next steps" banner.** It still prints -> `just relay` (a debug build). Use `sprout-relay` from step 2 here — +> `just relay` (a debug build). Use `buzz-relay` from step 2 here — > step 2 already built the release binary. When you're done, stop the relay (Ctrl-C in its terminal). If it's -backgrounded or you lost the terminal: `pkill -f sprout-relay`. Leaving +backgrounded or you lost the terminal: `pkill -f buzz-relay`. Leaving it running will collide with the next reviewer who follows this doc on the same machine. @@ -135,22 +135,22 @@ back. This is the minimum sequence an agent needs to verify a local relay. ```bash # Generate a keypair -GEN=$(sprout-admin generate-key) -export SPROUT_PRIVATE_KEY=$(echo "$GEN" | awk '/Secret key:/ {print $3}') +GEN=$(buzz-admin generate-key) +export BUZZ_PRIVATE_KEY=$(echo "$GEN" | awk '/Secret key:/ {print $3}') PUBKEY=$(echo "$GEN" | awk '/Public key:/ {print $3}') echo "pubkey: $PUBKEY" # Create a channel — the UUID is returned in the response -CHANNEL=$(sprout channels create --name "smoke-$$" --type stream --visibility open | jq -r '.channel_id') +CHANNEL=$(buzz channels create --name "smoke-$$" --type stream --visibility open | jq -r '.channel_id') echo "channel: $CHANNEL" # Send a message and read it back -SEND=$(sprout messages send --channel "$CHANNEL" --content "hello from smoke test") +SEND=$(buzz messages send --channel "$CHANNEL" --content "hello from smoke test") EVENT_ID=$(echo "$SEND" | jq -r '.event_id') -sprout messages get --channel "$CHANNEL" --limit 5 | jq . +buzz messages get --channel "$CHANNEL" --limit 5 | jq . # Fetch the reply chain for a specific message (empty array on a leaf — that's fine) -sprout messages thread --channel "$CHANNEL" --event "$EVENT_ID" | jq . +buzz messages thread --channel "$CHANNEL" --event "$EVENT_ID" | jq . ``` A successful run prints `{"event_id":"…","accepted":true,"message":""}` for @@ -160,10 +160,10 @@ for a leaf message — populated only after a reply comes in (see §5). ### 5. Going deeper For full coverage of every CLI command (54 subcommands across 12 groups), -follow [`crates/sprout-cli/TESTING.md`](crates/sprout-cli/TESTING.md). +follow [`crates/buzz-cli/TESTING.md`](crates/buzz-cli/TESTING.md). The relay's HTTP bridge accepts three endpoints — useful if you're testing -a client other than `sprout-cli`: +a client other than `buzz-cli`: | Endpoint | Purpose | |-----------------|------------------------------------| @@ -173,57 +173,57 @@ a client other than `sprout-cli`: All three accept NIP-98 auth (recommended) or, in dev mode, an `X-Pubkey` header fallback. There is no REST API for fetching message threads — use -`POST /query` with an `#e` filter, or `sprout messages thread`. +`POST /query` with an `#e` filter, or `buzz messages thread`. --- ## ACP Harness (optional, end-to-end with a real agent) -`sprout-acp` connects an ACP-speaking agent (goose, codex, claude code, -sprout-agent) to the relay. The harness listens for events, drives the +`buzz-acp` connects an ACP-speaking agent (goose, codex, claude code, +buzz-agent) to the relay. The harness listens for events, drives the agent over stdio, and the agent replies through MCP tools. Minimum recipe — assumes the relay from step 3 is running and the channel `$CHANNEL` from step 4 still exists. The agent identity must be **different** -from the sender identity (`SPROUT_ACP_RESPOND_TO=anyone` still skips events +from the sender identity (`BUZZ_ACP_RESPOND_TO=anyone` still skips events the agent signed itself). ```bash -cargo build --release -p sprout-acp +cargo build --release -p buzz-acp export PATH="$PWD/target/release:$PATH" # 1. Save your sender identity from step 4 — you'll need it to @mention the agent -SENDER_SK="$SPROUT_PRIVATE_KEY" +SENDER_SK="$BUZZ_PRIVATE_KEY" # 2. Mint a fresh agent identity and capture its pubkey -AGENT_GEN=$(sprout-admin generate-key) +AGENT_GEN=$(buzz-admin generate-key) AGENT_SK=$(echo "$AGENT_GEN" | awk '/Secret key:/ {print $3}') AGENT_PUBKEY=$(echo "$AGENT_GEN" | awk '/Public key:/ {print $3}') # 3. Add the agent as a member of $CHANNEL — still using the sender identity. # Skip this and the agent boots to "discovered 0 channel(s) → agent will # sit idle" and silently ignores every mention. -sprout channels add-member --channel "$CHANNEL" --pubkey "$AGENT_PUBKEY" --role member +buzz channels add-member --channel "$CHANNEL" --pubkey "$AGENT_PUBKEY" --role member # 4. Switch to the agent identity and start it. -# sprout-acp wants ws:// (not http://). If you set SPROUT_RELAY_URL to an +# buzz-acp wants ws:// (not http://). If you set BUZZ_RELAY_URL to an # http:// URL in step 3, set the ws:// equivalent here — same host/port. -export SPROUT_PRIVATE_KEY="$AGENT_SK" -export SPROUT_RELAY_URL=ws://localhost:3000 # match step 3 (e.g. ws://localhost:3030 if overridden) -export SPROUT_ACP_RESPOND_TO=anyone # default is owner-only; opens the gate for testing -# NIP-AE core-memory prompt injection is on by default; set SPROUT_ACP_NO_MEMORY=true to opt out. +export BUZZ_PRIVATE_KEY="$AGENT_SK" +export BUZZ_RELAY_URL=ws://localhost:3000 # match step 3 (e.g. ws://localhost:3030 if overridden) +export BUZZ_ACP_RESPOND_TO=anyone # default is owner-only; opens the gate for testing +# NIP-AE core-memory prompt injection is on by default; set BUZZ_ACP_NO_MEMORY=true to opt out. export GOOSE_MODE=auto # must be 'auto' or goose hangs on prompts -sprout-acp # foreground; logs to stdout (run in a separate terminal) +buzz-acp # foreground; logs to stdout (run in a separate terminal) # Optional: turn on per-turn tracing if the default log is too quiet. -# RUST_LOG=sprout_acp=debug sprout-acp +# RUST_LOG=buzz_acp=debug buzz-acp ``` > **Using a different ACP agent?** The default recipe assumes `goose` is on > `$PATH` and configured (`goose --version` should print). For codex / claude -> code / sprout-agent, set `SPROUT_ACP_AGENT_COMMAND` and `SPROUT_ACP_AGENT_ARGS` -> accordingly — see `crates/sprout-acp/README.md`. Without these, sprout-acp +> code / buzz-agent, set `BUZZ_ACP_AGENT_COMMAND` and `BUZZ_ACP_AGENT_ARGS` +> accordingly — see `crates/buzz-acp/README.md`. Without these, buzz-acp > will fail to spawn the agent subprocess on startup. If you started the agent before adding it to the channel, just run the @@ -232,24 +232,24 @@ subscribes without restart (`membership notification: subscribing to new channel The justfile also ships `just goose key="$AGENT_NSEC"` (foreground) and `just goose-bg key="$AGENT_NSEC"` (background screen session) which set the -same env. See `crates/sprout-acp/README.md` for parallel agents, heartbeats, +same env. See `crates/buzz-acp/README.md` for parallel agents, heartbeats, respond-to gates, and forum subscriptions. Send the agent a task — switch your shell back to the **sender** identity from step 4 and @mention the agent: ```bash -export SPROUT_PRIVATE_KEY=$SENDER_SK # the key from step 4 -sprout messages send --channel "$CHANNEL" \ +export BUZZ_PRIVATE_KEY=$SENDER_SK # the key from step 4 +buzz messages send --channel "$CHANNEL" \ --content "Hey agent, reply PONG only." # Wait 10–90s, then read the channel — the agent's reply is a kind:9 from # AGENT_PUBKEY. The current ACP build is quiet on stdout during a turn, so -# `sprout messages get` is how you confirm it ran. -sprout messages get --channel "$CHANNEL" --limit 5 | jq '.[] | {pubkey, content}' +# `buzz messages get` is how you confirm it ran. +buzz messages get --channel "$CHANNEL" --limit 5 | jq '.[] | {pubkey, content}' ``` -Replies are kind:9 in the same channel; `sprout messages thread --channel +Replies are kind:9 in the same channel; `buzz messages thread --channel --event ` fetches the reply chain for a specific mention. --- @@ -261,25 +261,25 @@ out of the box with `just setup` or `just relay`. Common overrides: | Variable | Default | Notes | |-----------------------------------|-----------------------------|-------| -| `SPROUT_BIND_ADDR` | `0.0.0.0:3000` | Main app port | -| `SPROUT_HEALTH_PORT` | `8080` | `/_liveness`, `/_readiness` | -| `SPROUT_METRICS_PORT` | `9102` | Prometheus `/metrics` | +| `BUZZ_BIND_ADDR` | `0.0.0.0:3000` | Main app port | +| `BUZZ_HEALTH_PORT` | `8080` | `/_liveness`, `/_readiness` | +| `BUZZ_METRICS_PORT` | `9102` | Prometheus `/metrics` | | `RELAY_URL` | `ws://localhost:3000` | Advertised in NIP-11 / NIP-42 challenges. **Note: no `SPROUT_` prefix.** | -| `DATABASE_URL` | `postgres://sprout:sprout_dev@localhost:5432/sprout` | | +| `DATABASE_URL` | `postgres://buzz:buzz_dev@localhost:5432/buzz` | | | `REDIS_URL` | `redis://localhost:6379` | | | `TYPESENSE_URL` | `http://localhost:8108` | | -| `SPROUT_REQUIRE_AUTH_TOKEN` | `false` | When true, REST requires NIP-98 (no `X-Pubkey` fallback) | -| `SPROUT_REQUIRE_RELAY_MEMBERSHIP` | `false` | When true, only pubkeys in `relay_members` can connect | +| `BUZZ_REQUIRE_AUTH_TOKEN` | `false` | When true, REST requires NIP-98 (no `X-Pubkey` fallback) | +| `BUZZ_REQUIRE_RELAY_MEMBERSHIP` | `false` | When true, only pubkeys in `relay_members` can connect | | `RELAY_OWNER_PUBKEY` | unset | Bootstrapped as `owner` in `relay_members` at first start | -| `SPROUT_ALLOW_NIP_OA_AUTH` | `false` | Enable NIP-OA owner attestation for membership | +| `BUZZ_ALLOW_NIP_OA_AUTH` | `false` | Enable NIP-OA owner attestation for membership | CLI-side, only two matter for testing: | Variable | Default | Notes | |-------------------------|--------------------------|-------| -| `SPROUT_RELAY_URL` | `http://localhost:3000` | CLI relay base; accepts `ws(s)://` and normalises | -| `SPROUT_PRIVATE_KEY` | — (**required**) | `nsec1…` or 64-char hex | -| `SPROUT_AUTH_TAG` | unset | Optional NIP-OA owner attestation JSON | +| `BUZZ_RELAY_URL` | `http://localhost:3000` | CLI relay base; accepts `ws(s)://` and normalises | +| `BUZZ_PRIVATE_KEY` | — (**required**) | `nsec1…` or 64-char hex | +| `BUZZ_AUTH_TAG` | unset | Optional NIP-OA owner attestation JSON | --- @@ -288,12 +288,12 @@ CLI-side, only two matter for testing: | Symptom | Cause | Fix | |---------|-------|-----| | `relay error 500` or `400: restricted: not a channel member` after a code change | Stale binary | Rebuild and re-export `PATH`; or `cargo run` directly | -| `Address already in use` on relay start (os error 48 on macOS, 98 on Linux) | Another relay (or stale process) holding `:3000` / `:8080` / `:9102` (or your override ports) | The panic line names the failing port — read it first. Then `lsof -iTCP:3000,8080,9102 -sTCP:LISTEN` (or your override equivalents). Kill the offender (`pkill -f sprout-relay`) or use the port-override block in step 3. If you already overrode and *still* collide, a prior reviewer left a relay running on the same alt ports — kill it or pick fresh ports | -| `auth_error: SPROUT_PRIVATE_KEY is required` | Env not exported into the CLI's shell | `export SPROUT_PRIVATE_KEY=...` (or pass `--private-key`) | -| `auth_error: SPROUT_AUTH_TAG verification failed … signature verification failed` | A stale `SPROUT_AUTH_TAG` inherited from a parent shell. The local dev relay rejects it. | `unset SPROUT_AUTH_TAG` (see the scrub block in step 1) | -| `auth-required: verification failed` on a closed relay | NIP-OA attestation needed | Set `SPROUT_AUTH_TAG` to the owner-issued JSON, or relax `SPROUT_REQUIRE_RELAY_MEMBERSHIP` | +| `Address already in use` on relay start (os error 48 on macOS, 98 on Linux) | Another relay (or stale process) holding `:3000` / `:8080` / `:9102` (or your override ports) | The panic line names the failing port — read it first. Then `lsof -iTCP:3000,8080,9102 -sTCP:LISTEN` (or your override equivalents). Kill the offender (`pkill -f buzz-relay`) or use the port-override block in step 3. If you already overrode and *still* collide, a prior reviewer left a relay running on the same alt ports — kill it or pick fresh ports | +| `auth_error: BUZZ_PRIVATE_KEY is required` | Env not exported into the CLI's shell | `export BUZZ_PRIVATE_KEY=...` (or pass `--private-key`) | +| `auth_error: BUZZ_AUTH_TAG verification failed … signature verification failed` | A stale `BUZZ_AUTH_TAG` inherited from a parent shell. The local dev relay rejects it. | `unset BUZZ_AUTH_TAG` (see the scrub block in step 1) | +| `auth-required: verification failed` on a closed relay | NIP-OA attestation needed | Set `BUZZ_AUTH_TAG` to the owner-issued JSON, or relax `BUZZ_REQUIRE_RELAY_MEMBERSHIP` | | `channels list` empty after `channels create` | The CLI doesn't echo the channel UUID; use the filter shown in step 4 | Or `POST /query` with `{"kinds":[39002]}` | -| ACP agent ignores all events | `SPROUT_ACP_RESPOND_TO=owner-only` (default) with no owner configured | Set `SPROUT_ACP_RESPOND_TO=anyone` for testing | -| ACP logs `discovered 0 channel(s)` / `no channel subscriptions resolved` | Agent identity isn't a member of any channel | `sprout channels add-member --channel "$CHANNEL" --pubkey "$AGENT_PUBKEY" --role member` from another identity | +| ACP agent ignores all events | `BUZZ_ACP_RESPOND_TO=owner-only` (default) with no owner configured | Set `BUZZ_ACP_RESPOND_TO=anyone` for testing | +| ACP logs `discovered 0 channel(s)` / `no channel subscriptions resolved` | Agent identity isn't a member of any channel | `buzz channels add-member --channel "$CHANNEL" --pubkey "$AGENT_PUBKEY" --role member` from another identity | | `GOOSE_MODE` warning, agent hangs | Not set | `export GOOSE_MODE=auto` | | Tests pass locally but CI fails | Forgot to run `just ci` | `just ci` runs the gate (fmt, clippy, unit tests, desktop/web builds) | diff --git a/examples/README.md b/examples/README.md index be87191b2..8649ba360 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,18 +1,18 @@ # Examples -This directory contains reference material for building on Sprout beyond the desktop app and AI agents. +This directory contains reference material for building on Buzz beyond the desktop app and AI agents. ## `countdown-bot/` -A small non-AI bot that connects directly to the Sprout relay over WebSocket, authenticates with NIP-42, subscribes to one channel, and replies to deterministic commands like `!countdown 5` and `!fib 8`. +A small non-AI bot that connects directly to the Buzz relay over WebSocket, authenticates with NIP-42, subscribes to one channel, and replies to deterministic commands like `!countdown 5` and `!fib 8`. It demonstrates two identity paths: 1. **Standalone bot identity** — the bot authenticates with its own key and must be explicitly admitted to closed/allowlisted relays. -2. **Owner-attested / agent OAuth path** — the bot authenticates with its own key while presenting the same `BUZZ_AUTH_TAG` NIP-OA credential that Sprout agents receive from the owner/agent OAuth flow, so a relay can admit it because its owner is already a relay member. +2. **Owner-attested / agent OAuth path** — the bot authenticates with its own key while presenting the same `BUZZ_AUTH_TAG` NIP-OA credential that Buzz agents receive from the owner/agent OAuth flow, so a relay can admit it because its owner is already a relay member. See [`countdown-bot/README.md`](countdown-bot/README.md) for usage. ## `meadow-core/` -A persona-pack example for Sprout agents. +A persona-pack example for Buzz agents. diff --git a/examples/countdown-bot/README.md b/examples/countdown-bot/README.md index fba78b2b8..09b3c1352 100644 --- a/examples/countdown-bot/README.md +++ b/examples/countdown-bot/README.md @@ -1,22 +1,22 @@ # Countdown Bot -A tiny non-AI Sprout bot example. +A tiny non-AI Buzz bot example. -The bot is deliberately boring and algorithmic: it listens to one Sprout channel +The bot is deliberately boring and algorithmic: it listens to one Buzz channel and replies to simple commands: - `!countdown 5` → `5 4 3 2 1 🚀` - `!fib 8` → `13 8 5 3 2 1 1 0` - `@Countdown Bot fib 8` → `13 8 5 3 2 1 1 0` -It demonstrates that Sprout participants do not have to be LLM agents. Any +It demonstrates that Buzz participants do not have to be LLM agents. Any process that can hold a Nostr key, answer NIP-42 auth, publish a kind `0` profile, subscribe to events, and publish kind `9` channel messages can be a bot. On startup it publishes a profile named **Countdown Bot** with a small embedded SVG clock icon, then best-effort publishes a NIP-29 `kind:9000` self-add with `role=bot`. That channel membership is what makes the bot show up in the -members list and in Sprout's mention autocomplete. +members list and in Buzz's mention autocomplete. ## Auth paths @@ -42,7 +42,7 @@ owner's access; revoking the bot requires removing this bot pubkey. The bot still signs messages with its own key, but its NIP-42 `AUTH` event also carries a NIP-OA `auth` tag signed by an owner key that is already allowed on the -relay. This reuses the same owner-attestation credential path that Sprout agents +relay. This reuses the same owner-attestation credential path that Buzz agents receive after the owner/agent OAuth flow: the relay can let the bot connect because the owner is a relay member, without making the bot key a persistent relay member. @@ -82,7 +82,7 @@ it to appear in members, resolve in mention autocomplete, or read/write messages ## Try it locally -1. Start Sprout: +1. Start Buzz: ```bash . ./bin/activate-hermit @@ -108,7 +108,7 @@ it to appear in members, resolve in mention autocomplete, or read/write messages make the bot spam the relay. Out-of-range commands get an explicit help reply. - `!fib` replies in descending order because this example is a countdown bot. - Mention commands require both text like `@Countdown Bot fib 8` and a `p` tag - for the bot pubkey. The Sprout UI adds that tag when the bot is selected from + for the bot pubkey. The Buzz UI adds that tag when the bot is selected from mention autocomplete. - The bot ignores its own messages to avoid feedback loops. - The example uses direct WebSocket + NIP-42 instead of MCP so the protocol path diff --git a/examples/meadow-core/README.md b/examples/meadow-core/README.md index e2d4db36b..416ea1510 100644 --- a/examples/meadow-core/README.md +++ b/examples/meadow-core/README.md @@ -1,6 +1,6 @@ # Meadow Core -A minimal three-agent persona pack for Sprout. +A minimal three-agent persona pack for Buzz. | Agent | Role | |-------|------| @@ -12,10 +12,10 @@ A minimal three-agent persona pack for Sprout. ```bash # Validate the pack -sprout pack validate ./examples/meadow-core +buzz pack validate ./examples/meadow-core # Inspect resolved config -sprout pack inspect ./examples/meadow-core +buzz pack inspect ./examples/meadow-core # Import into the desktop app # Use the "Install Pack" button and point to this directory @@ -44,4 +44,4 @@ Edit any `.persona.md` file to change the agent's behavior. The YAML frontmatter controls config (model, triggers, channels). The markdown body is the system prompt. -See `crates/sprout-persona/PERSONA_PACK_SPEC.md` for the full format reference. +See `crates/buzz-persona/PERSONA_PACK_SPEC.md` for the full format reference. diff --git a/scripts/cleanup-instance-agents.sh b/scripts/cleanup-instance-agents.sh index a6bc52f98..b40603b3c 100755 --- a/scripts/cleanup-instance-agents.sh +++ b/scripts/cleanup-instance-agents.sh @@ -2,7 +2,7 @@ # Reap the agent processes belonging to a single desktop instance. # # `tauri dev` Ctrl+C tears down the Rust app before its in-process system sweep -# can finish, so agent workers (goose, sprout-agent, ...) it spawned in their +# can finish, so agent workers (goose, buzz-agent, ...) it spawned in their # own process groups survive as orphans. This script is the shell-side backstop: # run it from an EXIT trap in the `just dev`/`just staging` recipes. # @@ -10,7 +10,7 @@ # under `/agents/agent-pids/.pid`, each containing the agent's # PGID (agents are spawned with `process_group(0)`, so PID == PGID). Killing by # PGID reaches the whole agent subtree. We deliberately do NOT match the -# `SPROUT_MANAGED_AGENT` env var from the shell: on macOS `pkill -f` matches only +# `BUZZ_MANAGED_AGENT` env var from the shell: on macOS `pkill -f` matches only # argv, not the environment, so an env-marker match silently reaps nothing. # # Scoping is exact because the app-data directory is keyed by the instance's @@ -18,8 +18,8 @@ # (the main checkout never reaps a worktree's agents, or vice versa). # # Usage: cleanup-instance-agents.sh -# is the desktop bundle identifier, e.g. `xyz.block.sprout.app.dev` -# (main checkout) or `xyz.block.sprout.app.dev.my-branch` (a worktree). +# is the desktop bundle identifier, e.g. `xyz.block.buzz.app.dev` +# (main checkout) or `xyz.block.buzz.app.dev.my-branch` (a worktree). set -euo pipefail