docs: nest 19 files into audit/ clients/ infrastructure/ monitoring/ projects/ super-search/
This commit is contained in:
@@ -0,0 +1,587 @@
|
||||
# Server-Side Agent Integration with Buzz
|
||||
|
||||
**Status:** Speculative / Research
|
||||
**Date:** 2026-08-07
|
||||
**Author:** Sho'Nuff
|
||||
**Relay:** `wss://buzz.iamgmb.com` (app3, Docker Compose)
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Buzz's native agent integration model (`buzz-acp`) is **desktop-centric**: it spawns ACP-compliant agent binaries as local subprocesses via stdio. Hermes is server-side (Core VPS) and cannot be spawned as a local binary on a user's laptop. This spec evaluates four integration paths to make Hermes a first-class participant in Buzz channels — able to receive @mentions and post replies — and recommends a Nostr-native WebSocket client approach modeled after the proven OpenClaw Buzz plugin.
|
||||
|
||||
---
|
||||
|
||||
## 1. ACP Protocol Research
|
||||
|
||||
### 1.1 What is ACP?
|
||||
|
||||
The **Agent Client Protocol (ACP)** is an open standard hosted at [agentclientprotocol.com](https://agentclientprotocol.com/), governed by a spec repo at [github.com/agentclientprotocol/agent-client-protocol](https://github.com/agentclientprotocol/agent-client-protocol). It is modeled after LSP (Language Server Protocol) and standardizes communication between code editors/IDEs and AI coding agents.
|
||||
|
||||
**Protocol fundamentals:**
|
||||
- **Wire format:** JSON-RPC 2.0 over stdio (primary transport today)
|
||||
- **Roles:** Client (editor/IDE/harness) ↔ Agent (AI coding tool)
|
||||
- **Lifecycle:** `initialize` → `session/new` → `session/prompt` → `session/update` (streaming) → `StopReason`
|
||||
- **Concepts:** Sessions, tool calls, cancellation, context window updates, authentication
|
||||
- **Rust crate:** [`acp-sdk`](https://crates.io/crates/acp-sdk) provides typed wire messages
|
||||
|
||||
**Key ACP methods:**
|
||||
|
||||
| Method | Direction | Purpose |
|
||||
|--------|-----------|---------|
|
||||
| `initialize` | Client → Agent | Handshake, negotiate protocol version + capabilities |
|
||||
| `session/new` | Client → Agent | Create session, pass cwd + MCP server configs |
|
||||
| `session/prompt` | Client → Agent | Send user prompt, agent loops LLM + tool calls |
|
||||
| `session/cancel` | Client → Agent | Cancel ongoing session |
|
||||
| `session/close` | Client → Agent | Close session, free resources |
|
||||
| `session/update` | Agent → Client | Streaming updates: tool calls, text chunks, usage |
|
||||
| `authenticate` | Client → Agent | Auth before session creation |
|
||||
|
||||
**AGENT NOTIFICATION — `buzz-agent` implementation:**
|
||||
- Single binary, ACP-compliant. Speaks MCP to tools (stdio only, no HTTP MCP).
|
||||
- Up to 8 concurrent sessions per process.
|
||||
- Non-streaming HTTP POST to LLM providers (Anthropic, OpenAI, OpenRouter).
|
||||
- Not persistent (in-memory per process), no `session/load`.
|
||||
|
||||
### 1.2 Remote Transport Status
|
||||
|
||||
**ACP remote transports are in active development but NOT shipped yet:**
|
||||
|
||||
- An RFD (Request for Discussion) exists at [agentclientprotocol.com/rfds/streamable-http-websocket-transport](https://agentclientprotocol.com/rfds/streamable-http-websocket-transport)
|
||||
- A **Transports Working Group** has been formed, co-led by Block/Goose and JetBrains
|
||||
- The RFD proposes:
|
||||
- **Streamable HTTP** (HTTP/2, long-lived GET streams, `Acp-Connection-Id` + `Acp-Session-Id` headers)
|
||||
- **WebSocket** (`GET /acp` with `Upgrade: websocket` header)
|
||||
- Unified `/acp` endpoint routing
|
||||
- This is an RFD, not implemented. No timeline published.
|
||||
|
||||
**Current reality:** ACP is stdio-only for production use. Remote agents are a documented goal, not a working feature.
|
||||
|
||||
### 1.3 How Buzz Uses ACP
|
||||
|
||||
Buzz's agent harness is **`buzz-acp`** — a Rust binary that bridges the Buzz relay to AI agents:
|
||||
|
||||
```
|
||||
┌──────────────┐ WebSocket ┌──────────┐ stdio ACP ┌───────────────┐
|
||||
│ Buzz Relay │ ◄────────────────► │ buzz-acp │ ◄───────────────► │ Agent Binary │
|
||||
│ (Nostr) │ (NIP-01 events) │ (harness)│ (JSON-RPC 2.0) │ (goose,codex, │
|
||||
└──────────────┘ └──────────┘ │ claude-code) │
|
||||
└───────────────┘
|
||||
```
|
||||
|
||||
**How `buzz-acp` works (from `.env.example` + source analysis):**
|
||||
|
||||
1. **Connects to relay** via WebSocket using `BUZZ_PRIVATE_KEY` (Nostr keypair, NIP-42/98 auth)
|
||||
2. **Subscribes to events** where the agent's pubkey appears in `p` tags (i.e., @mentions)
|
||||
- `BUZZ_ACP_SUBSCRIBE=mentions` (default) — subscribe only to events mentioning agent
|
||||
- `BUZZ_ACP_SUBSCRIBE=all` — subscribe to all channel events
|
||||
- `BUZZ_ACP_SUBSCRIBE=config` — rule-based via TOML config file
|
||||
3. **Spawns agent binary** as subprocess (Goose, Codex, Claude Code, or any ACP agent)
|
||||
- `BUZZ_ACP_AGENT_COMMAND` / `BUZZ_ACP_AGENT_ARGS`
|
||||
4. **Forwards prompts** to agent via ACP `session/prompt`, streams results back to relay
|
||||
5. **Manages presence** (kind 20001 online/offline), typing indicators (kind 20002), dedup
|
||||
|
||||
**Key insight:** `buzz-acp` itself IS the WebSocket-to-stdio bridge. It doesn't expose a remote API — it IS the client that connects to the relay and spawns agents. There is **no existing `buzz-acp` HTTP API** to connect remote agents to.
|
||||
|
||||
### 1.4 The @mention Mechanism
|
||||
|
||||
In Buzz/Nostr, "mentioning" an agent means including its Nostr pubkey as a `p` tag in a channel message event. The relay's subscription registry fans out matching events to all subscribed WebSocket clients. `buzz-acp` subscribes with a filter like `{"#p": [agent_pubkey]}` and receives all events that tag that pubkey. There is **no special server-side routing** — it's standard Nostr subscription fan-out.
|
||||
|
||||
---
|
||||
|
||||
## 2. Integration Architecture Options
|
||||
|
||||
### 2.1 Option A: Bridge Agent (Stdio ACP Proxy)
|
||||
|
||||
Deploy a lightweight binary on Core (or app3) that:
|
||||
1. Implements the ACP client side (speaks JSON-RPC 2.0 over stdio to a dummy agent)
|
||||
2. OR implements the ACP agent side (so `buzz-acp` can spawn it) that proxies to Hermes
|
||||
|
||||
```
|
||||
┌──────────┐ WS ┌──────────┐ stdio ACP ┌──────────────┐ HTTP/WS ┌──────────┐
|
||||
│ Relay │◄─────►│ buzz-acp │◄──────────►│ Bridge Binary │◄────────►│ Hermes │
|
||||
└──────────┘ └──────────┘ └──────────────┘ └──────────┘
|
||||
(runs on Core)
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
- `buzz-acp` spawns the bridge binary as an ACP agent subprocess
|
||||
- Bridge binary receives ACP `session/prompt` containing the user's message
|
||||
- Bridge forwards it to Hermes via REST API or WebSocket
|
||||
- Hermes processes, returns response
|
||||
- Bridge sends response back through ACP `session/update` notifications
|
||||
|
||||
**Pros:**
|
||||
- Uses Buzz's native agent machinery (presence, typing, turn lifecycle)
|
||||
- Agent appears in Buzz Desktop's agent panel naturally
|
||||
- Gets @mention routing for free via `buzz-acp`
|
||||
|
||||
**Cons:**
|
||||
- `buzz-acp` must run on a machine that can reach Hermes (not a laptop — would need to run on Core or app3)
|
||||
- Stdio bridge is fragile (subprocess lifecycle, crash recovery, binary distribution)
|
||||
- ACP is designed for local coding agents, not remote conversational agents — impedance mismatch
|
||||
- Bridge must implement full ACP agent spec (initialize, sessions, tool calls, cancellation)
|
||||
- `buzz-acp` is a desktop-side component — running it headless on a VPS is an off-label use
|
||||
- Requires compiling and maintaining a Rust binary (ACP SDK crate)
|
||||
|
||||
**Effort:** High. Requires implementing an ACP-compliant agent from scratch.
|
||||
|
||||
### 2.2 Option B: Nostr-Native WebSocket Client (RECOMMENDED)
|
||||
|
||||
Hermes connects directly to the Buzz relay as a Nostr WebSocket client with its own keypair — exactly how `buzz-acp` and the Buzz Desktop app connect.
|
||||
|
||||
```
|
||||
┌──────────┐ WebSocket (NIP-01/42/98) ┌──────────┐
|
||||
│ Relay │◄─────────────────────────────────────►│ Hermes │
|
||||
└──────────┘ Signed Nostr events (kind 9) └──────────┘
|
||||
@mentions via p-tag subscriptions
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
1. Hermes generates or is assigned a Nostr keypair (pubkey = Buzz identity)
|
||||
2. Hermes connects to `wss://buzz.iamgmb.com` via WebSocket
|
||||
3. Hermes authenticates via NIP-42 (signed AUTH challenge) or NIP-98 (HTTP auth)
|
||||
4. Hermes subscribes to events with `{"#p": [hermes_pubkey]}` — receives all @mentions
|
||||
5. When a mention arrives, Hermes routes it to its AI pipeline, generates a response
|
||||
6. Hermes publishes a signed Nostr event (kind 9 or 40002) back to the same channel
|
||||
7. Hermes manages presence (kind 20001) and typing indicators (kind 20002)
|
||||
|
||||
**Proof of concept: OpenClaw Buzz Plugin**
|
||||
[OpenClaw's Buzz channel plugin](https://docs.openclaw.ai/channels/buzz) does exactly this. It connects an OpenClaw gateway (server-side agent platform) to Buzz as a Nostr client. Key details from their docs:
|
||||
|
||||
- Connects to relay via WebSocket with a dedicated Nostr keypair
|
||||
- Bot identity must be added to rooms with **Bot** role via `buzz channels add-member --role bot`
|
||||
- Subscribes to room events, handles kind 9 (normal messages), kind 40002 (rich-content), kind 40008 (structured diffs)
|
||||
- Publishes presence every 30 seconds
|
||||
- Sends typing indicators (kind 20002) while processing
|
||||
- Supports NIP-27 native mentions in replies
|
||||
- Handles reconnection, dedup, and stale session recovery
|
||||
- One identity can serve many rooms
|
||||
|
||||
**Pros:**
|
||||
- **Architecturally correct** — Buzz IS a Nostr relay. Connecting as a Nostr client is the first-class path.
|
||||
- No desktop dependency — runs entirely server-side
|
||||
- Proven pattern (OpenClaw already does this successfully)
|
||||
- Hermes gets full Buzz citizenship: presence, typing, reactions, profile, DMs
|
||||
- Uses standard protocols: WebSocket + JSON (NIP-01), Schnorr signatures
|
||||
- No ACP impedance mismatch — Hermes processes messages its own way
|
||||
- Can be implemented in Python (websockets + nostr-py or `secp256k1` bindings)
|
||||
- Coexists with other agents — Hermes is just another pubkey in the channel
|
||||
- Reuses Hermes's existing AI pipeline, tools, and skills
|
||||
|
||||
**Cons:**
|
||||
- Must implement Nostr protocol handling (event signing, subscription management, NIP-42 auth)
|
||||
- Does NOT use Buzz's native ACP agent panel UI — Hermes appears as a "bot" member, not a managed agent
|
||||
- No turn lifecycle management (ACP's `session/prompt` → `end_turn` model)
|
||||
- Must handle WebSocket reconnection, event dedup, and subscription state
|
||||
- Nostr python libraries are less mature than JS/Rust ecosystems
|
||||
|
||||
**Effort:** Medium. Requires a Nostr client module in Python (~500-800 lines).
|
||||
|
||||
### 2.3 Option C: Webhook Adapter (Buzz Workflows)
|
||||
|
||||
Use Buzz's YAML workflow engine to detect @mentions and fire webhooks to Hermes's REST API.
|
||||
|
||||
```
|
||||
┌──────────┐ Buzz Workflow ┌─────────────┐ HTTP POST ┌──────────┐
|
||||
│ Relay │────────►────────►│ Workflow │────────────►│ Hermes │
|
||||
│ (event) │ trigger on │ Engine │ webhook │ REST API │
|
||||
└──────────┘ kind 9 + p-tag └──────┬───────┘ └────┬─────┘
|
||||
│ │
|
||||
┌──────▼───────┐ ┌──────▼─────┐
|
||||
│ Response │◄─────────│ AI reply │
|
||||
│ back to │ REST API │ generated │
|
||||
│ channel │ └────────────┘
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
1. Create a Buzz workflow YAML that triggers on new messages in specific channels
|
||||
2. Workflow filter matches events where `p` tag includes Hermes's pubkey
|
||||
3. On match, workflow fires a `webhook` action to Hermes's REST API
|
||||
4. Hermes processes the message and generates a response
|
||||
5. Response is posted back to the channel via `buzz-cli` or relay REST API (NIP-98 signed)
|
||||
|
||||
**Buzz workflow capabilities (from ARCHITECTURE.md):**
|
||||
- Triggers: message, reaction, schedule, webhook
|
||||
- Actions: send message, add reaction
|
||||
- `send_dm` and `set_channel_topic` actions are stubbed (return `NotImplemented`)
|
||||
- Approval gates partially wired (WF-08: runs hitting approval gates fail)
|
||||
|
||||
**Pros:**
|
||||
- Zero new protocol code — uses HTTP webhooks and REST API
|
||||
- Leverages existing Buzz features (workflows are YAML-defined, relay-managed)
|
||||
- Simple mental model — "when someone @mentions Hermes, POST to this URL"
|
||||
- Hermes's existing REST API can be the webhook target
|
||||
- No Nostr key management for Hermes (workflow signs events on its behalf)
|
||||
|
||||
**Cons:**
|
||||
- **Workflow engine has gaps:** `send_dm` and `set_channel_topic` return `NotImplemented` (ARCHITECTURE.md §9, WF-07). Approval gates are partially broken (WF-08). Unknown if webhook→Hermes→response path works end-to-end.
|
||||
- Workflow execution latency — not real-time; workflow engine processes events on a schedule
|
||||
- Workflows can only react to events, not participate — no typing indicators, presence, or ongoing conversation state
|
||||
- Hermes would not have its own Nostr identity — it's the workflow acting on its behalf
|
||||
- No conversational context — each @mention is a fresh workflow run
|
||||
- The workflow engine is undergoing active development; breaking changes possible
|
||||
- Rate limiting unknown for workflow-triggered actions
|
||||
|
||||
**Effort:** Low to prototype, high risk of hitting engine limitations.
|
||||
|
||||
### 2.4 Option D: Future ACP Remote Transport
|
||||
|
||||
Wait for the ACP Transports Working Group to ship the Streamable HTTP / WebSocket remote transport, then have Hermes implement the ACP agent side over that transport.
|
||||
|
||||
**Status:** RFD stage — no timeline, no implementation.
|
||||
|
||||
**Pros:**
|
||||
- Eventually the "right" answer — fully standards-compliant
|
||||
- Hermes would be a first-class managed agent in Buzz Desktop
|
||||
- Remote transport is being designed for exactly this use case
|
||||
|
||||
**Cons:**
|
||||
- **Does not exist yet.** Building anything that depends on it today is blocked.
|
||||
- Timeline unknown — could be months or years
|
||||
- Would still need to implement ACP agent protocol (not just transport)
|
||||
- ACP is coding-agent-optimized; conversational agents are a secondary concern
|
||||
|
||||
**Effort:** Blocked. Cannot proceed until spec is finalized and implemented.
|
||||
|
||||
---
|
||||
|
||||
## 3. Comparison Matrix
|
||||
|
||||
| Criterion | Bridge Agent (A) | Nostr-Native (B) | Webhook (C) | Future ACP (D) |
|
||||
|-----------|:---:|:---:|:---:|:---:|
|
||||
| **Works today** | ⚠️ Off-label | ✅ Proven (OpenClaw) | ⚠️ Workflow gaps | ❌ Doesn't exist |
|
||||
| **Deployment complexity** | High (Rust binary) | Medium (Python module) | Low (YAML + HTTP) | Unknown |
|
||||
| **Latency** | Low (WebSocket → stdio) | Low (WebSocket native) | Medium-High (workflow poll) | Low |
|
||||
| **Reliability** | Medium (subprocess mgmt) | High (direct WS) | Low (engine gaps) | Unknown |
|
||||
| **Buzz agent UX** | Full (ACP panel) | Bot member (no ACP panel) | None (workflow) | Full (ACP panel) |
|
||||
| **Hermes identity** | Via buzz-acp key | Own Nostr keypair | Relay-owned (workflow) | Own ACP identity |
|
||||
| **Presence/typing** | ✅ | ✅ | ❌ | ✅ |
|
||||
| **Conversational context** | Via ACP sessions | App-level state | ❌ (per-invocation) | Via ACP sessions |
|
||||
| **Maintenance burden** | High | Medium | Low (but fragile) | Unknown |
|
||||
| **Protocol maturity** | ACP v1 (stable) | NIPs (stable) | Buzz workflows (beta) | ACP remote (pre-RFC) |
|
||||
| **Coexists w/ other agents** | ✅ | ✅ | ✅ | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 4. Recommended Path: Nostr-Native WebSocket Client
|
||||
|
||||
### 4.1 Justification
|
||||
|
||||
The Nostr-native approach is recommended for the following reasons:
|
||||
|
||||
1. **Architectural correctness.** Buzz IS a Nostr relay. Connecting as a Nostr client is the protocol's first-class integration path. The relay doesn't distinguish between "human," "agent," or "bot" — all are Nostr keypairs publishing signed events. Hermes joining as another keypair is exactly how Buzz was designed to work.
|
||||
|
||||
2. **Proven in production.** OpenClaw's Buzz plugin has already solved this exact problem — connecting a server-side AI agent platform to Buzz channels via WebSocket. Their docs describe a working implementation with presence, typing indicators, mention handling, and reconnection logic.
|
||||
|
||||
3. **No desktop dependency.** This approach runs entirely on Core. No `buzz-acp` binary needed. No ACP stdio bridge. No subprocess lifecycle management.
|
||||
|
||||
4. **Full Buzz citizenship.** Hermes gets its own Nostr identity, can have a profile (kind 0), presence status, typing indicators, and can participate in any channel it's added to.
|
||||
|
||||
5. **No blocking dependencies.** The Nostr protocol is stable (NIP-01, NIP-42, NIP-98). The ACP remote transport is not.
|
||||
|
||||
6. **Leverages existing Hermes infrastructure.** Hermes already has a REST API, Telegram integration, MCP tools, and an AI pipeline. The Nostr client becomes another input/output channel alongside those.
|
||||
|
||||
7. **Coexistence.** If Buzz later ships remote ACP transport, a Nostr-native Hermes can operate alongside ACP-managed agents. The two approaches are complementary, not mutually exclusive.
|
||||
|
||||
**Trade-offs accepted:**
|
||||
- Hermes appears as a "Bot" member in Buzz, not in the managed-agent ACP panel
|
||||
- No turn lifecycle management from Buzz's perspective (Hermes manages its own conversational state)
|
||||
- Must maintain WebSocket connection health (but this is standard infrastructure)
|
||||
|
||||
### 4.2 What "Bot" Member Means in Practice
|
||||
|
||||
In Buzz, a bot member with a Nostr keypair:
|
||||
- Can be @mentioned like any other member
|
||||
- Can post messages, reactions, and edits
|
||||
- Has an online/offline presence indicator
|
||||
- Shows typing indicators while processing
|
||||
- Can be added to or removed from channels
|
||||
- Has a profile (display name, avatar)
|
||||
- Appears in the member list with a "Bot" role badge
|
||||
- Cannot be spawned/managed via ACP (no agent panel controls)
|
||||
|
||||
This is functionally equivalent to how Slack bots, Discord bots, or Telegram bots work — they're members of the room, not subprocesses managed by the client.
|
||||
|
||||
---
|
||||
|
||||
## 5. Implementation Outline
|
||||
|
||||
### 5.1 Components
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ Core (Hermes VPS) │
|
||||
│ │
|
||||
│ ┌─────────────────┐ ┌──────────────────────────────┐ │
|
||||
│ │ Hermes Core │◄───►│ Buzz Nostr Client Module │ │
|
||||
│ │ (AI pipeline, │ │ │ │
|
||||
│ │ tools, skills) │ │ ┌──────────┐ ┌───────────┐ │ │
|
||||
│ │ │ │ │ WS Conn │ │ Event Sign │ │ │
|
||||
│ │ │ │ │ Manager │ │ er (Schnorr│ │ │
|
||||
│ │ │ │ └──────────┘ └───────────┘ │ │
|
||||
│ │ │ │ ┌──────────┐ ┌───────────┐ │ │
|
||||
│ │ │ │ │ Sub Mgmt │ │ Presence │ │ │
|
||||
│ │ │ │ └──────────┘ └───────────┘ │ │
|
||||
│ └─────────────────┘ └──────────────┬───────────────┘ │
|
||||
│ │ │
|
||||
└─────────────────────────────────────────┼─────────────────────┘
|
||||
│ WebSocket (WSS)
|
||||
│ NIP-01 events
|
||||
┌─────▼──────┐
|
||||
│ Buzz Relay │
|
||||
│ (app3) │
|
||||
└────────────┘
|
||||
```
|
||||
|
||||
**New components:**
|
||||
1. **`buzz_client.py`** — Nostr WebSocket client module (~500 lines)
|
||||
- WebSocket connection management (connect, reconnect, heartbeat)
|
||||
- NIP-42 authentication (sign AUTH challenge)
|
||||
- Event signing (Schnorr signatures via `secp256k1` or `nostr-py`)
|
||||
- Subscription management (REQ, CLOSE, EVENT delivery)
|
||||
- Event publishing (EVENT → relay)
|
||||
|
||||
2. **`buzz_channel.py`** — Hermes channel adapter (~200 lines)
|
||||
- Bridges Buzz events ↔ Hermes message pipeline
|
||||
- Filters events (ignore self, dedup by event ID)
|
||||
- Converts Nostr events to Hermes internal message format
|
||||
- Routes Hermes responses back to Buzz channels
|
||||
- Manages presence updates (30s interval)
|
||||
|
||||
3. **Buzz identity** — one Nostr keypair
|
||||
- Generated via `buzz-admin generate-key` on app3
|
||||
- Private key stored in Hermes secrets/env
|
||||
- Public key added to relay membership and target channels
|
||||
|
||||
### 5.2 Protocols & Wire Format
|
||||
|
||||
**Connection:**
|
||||
```
|
||||
Client Relay (wss://buzz.iamgmb.com)
|
||||
│ WebSocket connect │
|
||||
│─────────────────────────────────────────►│
|
||||
│ ← AUTH challenge │
|
||||
│◄─────────────────────────────────────────│
|
||||
│ AUTH response (signed challenge) │
|
||||
│─────────────────────────────────────────►│
|
||||
│ ← AUTH OK │
|
||||
│◄─────────────────────────────────────────│
|
||||
```
|
||||
|
||||
**Subscription (NIP-01 REQ):**
|
||||
```json
|
||||
["REQ", "hermes-mentions", {"#p": ["<hermes_pubkey_hex>"], "kinds": [9, 40002], "since": <last_seen_timestamp>}]
|
||||
```
|
||||
|
||||
**Message format (kind 9 — NIP-29 group chat):**
|
||||
```json
|
||||
{
|
||||
"id": "<sha256>",
|
||||
"pubkey": "<sender_pubkey>",
|
||||
"kind": 9,
|
||||
"tags": [
|
||||
["h", "<channel_uuid>"],
|
||||
["p", "<hermes_pubkey>"],
|
||||
["e", "<thread_root>", "", "reply"]
|
||||
],
|
||||
"content": "{\"text\": \"@Hermes what's the status of the backup?\"}",
|
||||
"sig": "<schnorr_sig>",
|
||||
"created_at": 1234567890
|
||||
}
|
||||
```
|
||||
|
||||
**Response message (kind 9):**
|
||||
```json
|
||||
{
|
||||
"id": "<sha256>",
|
||||
"pubkey": "<hermes_pubkey>",
|
||||
"kind": 9,
|
||||
"tags": [
|
||||
["h", "<channel_uuid>"],
|
||||
["e", "<thread_root>", "", "reply"],
|
||||
["p", "<requester_pubkey>"]
|
||||
],
|
||||
"content": "{\"text\": \"The backup completed successfully at 03:00 UTC. Latest snapshot: backup-2026-08-07.tar.gz\"}",
|
||||
"sig": "<schnorr_sig>",
|
||||
"created_at": 1234567895
|
||||
}
|
||||
```
|
||||
|
||||
**Presence (kind 20001, ephemeral, not stored):**
|
||||
```json
|
||||
["EVENT", {
|
||||
"kind": 20001,
|
||||
"content": "{\"status\": \"online\"}",
|
||||
"tags": [],
|
||||
...
|
||||
}]
|
||||
```
|
||||
|
||||
**Typing indicator (kind 20002, ephemeral):**
|
||||
```json
|
||||
["EVENT", {
|
||||
"kind": 20002,
|
||||
"content": "",
|
||||
"tags": [["h", "<channel_uuid>"]],
|
||||
...
|
||||
}]
|
||||
```
|
||||
|
||||
### 5.3 Auth Model
|
||||
|
||||
**Nostr keypair:**
|
||||
- Generate via `buzz-admin generate-key` on app3 (or `openssl rand -hex 32` for privkey → derive pubkey via secp256k1)
|
||||
- Hermes holds the private key (nsec or hex) in environment/secrets
|
||||
- Public key (64-char hex) is used for:
|
||||
- Relay membership: `./run.sh add-member <hermes_pubkey> --role member`
|
||||
- Channel membership: `buzz channels add-member --channel <uuid> --pubkey <hermes_pubkey> --role bot`
|
||||
- NIP-98 HTTP auth for REST API calls (if using REST fallback)
|
||||
|
||||
**NIP-42 authentication flow:**
|
||||
1. Relay sends `["AUTH", "<challenge_string>"]` on WebSocket connect
|
||||
2. Hermes constructs a kind 22242 auth event: `{"kind": 22242, "tags": [["challenge", challenge], ["relay", "wss://buzz.iamgmb.com"]], "content": "", ...}`
|
||||
3. Hermes signs the event with its private key (Schnorr)
|
||||
4. Hermes sends `["AUTH", <signed_event>]` to relay
|
||||
5. Relay verifies signature and pubkey membership → connection authenticated
|
||||
|
||||
**API token alternative:**
|
||||
Buzz supports API tokens as an alternative to NIP-42/NIP-98 for service accounts. This would replace the WebSocket auth dance with a static bearer token. However, API tokens are less documented and may not support all event kinds.
|
||||
|
||||
### 5.4 Deployment
|
||||
|
||||
| Component | Location | Details |
|
||||
|-----------|----------|---------|
|
||||
| Buzz Nostr client module | Core (Hermes VPS) | Python module imported by Hermes; runs in-process |
|
||||
| Nostr keypair | Core (secrets) | Private key in `.env` or HashiCorp Vault |
|
||||
| Relay membership | app3 | `./run.sh add-member` once during setup |
|
||||
| Channel membership | app3 (via buzz-cli) | `buzz channels add-member --role bot` per channel |
|
||||
| WebSocket connection | Core → app3:443 | WSS through CloudPanel Nginx |
|
||||
|
||||
**Note:** The WebSocket connection goes through CloudPanel's Nginx reverse proxy (`wss://buzz.iamgmb.com`). CloudPanel already includes WebSocket upgrade headers — no Nginx config changes needed.
|
||||
|
||||
### 5.5 Python Dependencies
|
||||
|
||||
| Package | Purpose |
|
||||
|---------|---------|
|
||||
| `websockets` | Async WebSocket client |
|
||||
| `secp256k1` (or `coincurve`) | Schnorr signature signing/verification |
|
||||
| `cryptography` | SHA-256 hashing for event IDs |
|
||||
| `bech32` | npub/nsec encoding (optional, for UX) |
|
||||
|
||||
**Or:** Use `nostr-py` / `python-nostr` if they're mature enough. Research needed.
|
||||
|
||||
### 5.6 Effort Estimate
|
||||
|
||||
| Phase | Work | Est. Days |
|
||||
|-------|------|-----------|
|
||||
| **Prototype** | Nostr event signing + WebSocket connect + basic REQ/EVENT | 2-3 |
|
||||
| **Channel adapter** | Message routing, dedup, mention detection, response posting | 2-3 |
|
||||
| **Polish** | Presence, typing indicators, reconnection, error handling | 2-3 |
|
||||
| **Integration** | Wire into Hermes's message pipeline + tool access | 2-3 |
|
||||
| **Testing** | Multi-channel, concurrent mentions, reconnect scenarios | 2-3 |
|
||||
| **Total** | | **10-15 days** |
|
||||
|
||||
This assumes the developer is familiar with Nostr protocol basics and Python async programming.
|
||||
|
||||
### 5.7 Alternate: Use `buzz-cli` as a Thin Proxy
|
||||
|
||||
As a lower-effort starting point, Hermes could use the existing `buzz-cli` binary for outbound messaging (posting replies) instead of implementing Nostr event signing from scratch:
|
||||
|
||||
```python
|
||||
# Post a reply via buzz-cli
|
||||
subprocess.run([
|
||||
"buzz", "messages", "send",
|
||||
"--channel", channel_uuid,
|
||||
"--content", response_text,
|
||||
"--reply-to", thread_event_id
|
||||
], env={"BUZZ_RELAY_URL": "wss://buzz.iamgmb.com", "BUZZ_PRIVATE_KEY": hermes_nsec})
|
||||
```
|
||||
|
||||
This avoids implementing Schnorr signing in Python but still requires a separate mechanism for **listening** to inbound mentions (since `buzz-cli` is request-response, not a persistent listener). The WebSocket subscription must still be implemented.
|
||||
|
||||
---
|
||||
|
||||
## 6. Open Questions
|
||||
|
||||
### 6.1 Must-Answer Before Building
|
||||
|
||||
| # | Question | How to Answer |
|
||||
|---|----------|---------------|
|
||||
| Q1 | **Does `buzz-cli` support a persistent listen/subscribe mode?** Current docs show only REST commands. If it has a hidden `buzz listen` or `buzz stream` mode, the implementation simplifies dramatically. | Search `buzz-cli/src/` for listen/stream/subscribe; test with `buzz help` |
|
||||
| Q2 | **What Python Nostr library is production-ready?** `nostr-py`, `python-nostr`, `nostr-sdk`? We need WebSocket client + Schnorr signing + NIP-42 auth. | Test each library against `wss://buzz.iamgmb.com` with a test keypair |
|
||||
| Q3 | **Can a non-ACP agent get the "Bot" role and appear in the member list?** OpenClaw does this, but need to verify exact permissions/UX. | Test with a manually-generated keypair added via `buzz channels add-member --role bot` |
|
||||
| Q4 | **What happens when an agent is @mentioned in a channel it hasn't joined?** Does the relay deliver the event anyway? Does Buzz Desktop show it? | Test by subscribing to #p tag without channel membership |
|
||||
| Q5 | **How does message threading work for agents?** Can Hermes reply in-thread by including the root event tag? | Examine OpenClaw's threading implementation; test manually |
|
||||
| Q6 | **What's the rate limit for agent-standard tier?** Config defaults show 120 messages/min, but enforcement is stubbed (`AlwaysAllowRateLimiter`). | Check if rate limiting is enforced in our relay version |
|
||||
|
||||
### 6.2 Would-Be-Nice Answers
|
||||
|
||||
| # | Question |
|
||||
|---|----------|
|
||||
| Q7 | When will the ACP remote transport ship? (Informs whether to invest in Nostr-native or wait for ACP) |
|
||||
| Q8 | Can Buzz workflows be used as a reliable event bridge, or are the `NotImplemented` stubs blocking? |
|
||||
| Q9 | Does the relay's REST API support subscribing to events via long-poll or SSE? (Alternative to WebSocket for listening) |
|
||||
| Q10 | Can Hermes's profile (kind 0) include custom metadata that Buzz Desktop renders (e.g., "AI Assistant" badge)? |
|
||||
| Q11 | How does agent-to-agent communication work in Buzz? Can Hermes @mention another agent? |
|
||||
| Q12 | What's the multi-community story? If we host multiple Buzz communities on the same relay, can one Hermes identity participate in all? |
|
||||
|
||||
---
|
||||
|
||||
## 7. References
|
||||
|
||||
| Resource | URL |
|
||||
|----------|-----|
|
||||
| Buzz GitHub | https://github.com/block/buzz |
|
||||
| Buzz README | https://github.com/block/buzz/blob/main/README.md |
|
||||
| Buzz Architecture | https://github.com/block/buzz/blob/main/ARCHITECTURE.md |
|
||||
| Buzz Agent Vision | https://github.com/block/buzz/blob/main/VISION_AGENT.md |
|
||||
| buzz-acp crate | https://github.com/block/buzz/tree/main/crates/buzz-acp |
|
||||
| buzz-cli crate | https://github.com/block/buzz/tree/main/crates/buzz-cli |
|
||||
| buzz-agent crate | https://github.com/block/buzz/blob/main/crates/buzz-agent/README.md |
|
||||
| ACP Specification | https://agentclientprotocol.com/ |
|
||||
| ACP Schema | https://agentclientprotocol.com/protocol/v1/schema |
|
||||
| ACP Remote Transport RFD | https://agentclientprotocol.com/rfds/streamable-http-websocket-transport |
|
||||
| ACP GitHub | https://github.com/agentclientprotocol/agent-client-protocol |
|
||||
| Buzz .env.example | https://github.com/block/buzz/blob/main/.env.example |
|
||||
| OpenClaw Buzz Plugin | https://docs.openclaw.ai/channels/buzz |
|
||||
| Buzz Self-Host Guide | https://engineering.block.xyz/blog/run-your-own-buzz-relay |
|
||||
| Buzz Skill (internal) | `~/.hermes/skills/devops/buzz-self-hosted-relay/SKILL.md` |
|
||||
| Our relay deployment | `/opt/buzz/deploy/compose` on app3 (152.53.241.111) |
|
||||
|
||||
---
|
||||
|
||||
## Appendix A: Nostr NIPs Used by Buzz
|
||||
|
||||
From ARCHITECTURE.md and source analysis:
|
||||
|
||||
| NIP | Name | Buzz Usage |
|
||||
|-----|------|------------|
|
||||
| NIP-01 | Basic protocol | Event format, REQ/EVENT/CLOSE messages |
|
||||
| NIP-02 | Contact list | User contacts/follows |
|
||||
| NIP-05 | DNS-based identity | `/.well-known/nostr.json` |
|
||||
| NIP-11 | Relay info | `GET /` returns relay metadata |
|
||||
| NIP-16 | Replaceable events | Profile (kind 0), channel metadata |
|
||||
| NIP-25 | Reactions | Kind 7 emoji reactions |
|
||||
| NIP-27 | Text note references | `nostr:npub1...` and `nostr:note1...` |
|
||||
| NIP-29 | Group chat | Kind 9 stream messages |
|
||||
| NIP-34 | Git hosting | Repository announcements, patches |
|
||||
| NIP-38 | User statuses | Profile status text+emoji |
|
||||
| NIP-42 | Auth | `AUTH` challenge-response on WebSocket |
|
||||
| NIP-98 | HTTP Auth | Schnorr-signed kind 27235 for REST API |
|
||||
|
||||
## Appendix B: Buzz Custom Event Kinds
|
||||
|
||||
| Kind | Name | Description |
|
||||
|------|------|-------------|
|
||||
| 9 | Stream message | Channel chat message (NIP-29) |
|
||||
| 7 | Reaction | Emoji reaction (NIP-25) |
|
||||
| 20001 | Presence | Ephemeral online/away status |
|
||||
| 20002 | Typing | Ephemeral typing indicator |
|
||||
| 22242 | Auth | NIP-42 authentication event |
|
||||
| 27235 | HTTP Auth | NIP-98 HTTP authentication |
|
||||
| 40002 | Stream message v2 | Rich-content channel message |
|
||||
| 40003 | Stream message edit | Edit of a previous message |
|
||||
| 40008 | Structured diff | Code diff with metadata |
|
||||
| 43001 | Job request | Agent job request (ACP) |
|
||||
| 40100 | Canvas | Channel canvas content |
|
||||
Reference in New Issue
Block a user