diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 90cbbac0cf..7b3ec7bbc8 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -825,3 +825,8 @@ These are verified gaps in the current implementation β€” not design aspirations | 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). | | 6 | **Workflow actions partially stubbed** | The `send_dm` and `set_channel_topic` workflow actions are in the schema but return `NotImplemented` β€” a run that reaches one fails at execution (🚧 WF-07). | + +## Self-host backend framework + +For the operator-facing always-on agents + any-client layout, see +[docs/self-host-backend-framework.md](docs/self-host-backend-framework.md). diff --git a/crates/buzz-acp/README.md b/crates/buzz-acp/README.md index e6164b02dd..775bb2a410 100644 --- a/crates/buzz-acp/README.md +++ b/crates/buzz-acp/README.md @@ -338,3 +338,12 @@ See the [root TESTING.md](../../TESTING.md) for the full integration testing gui ## License Apache-2.0 + +## Always-on agents (any client) + +For **server-side `buzz-acp`** with Desktop, web, mobile, or CLI as thin clients +(agents stay up when devices sleep or disconnect), see: + +- **Full framework:** [docs/self-host-backend-framework.md](../../docs/self-host-backend-framework.md) +- **Quick start:** [docs/self-host-always-on-agents.md](../../docs/self-host-always-on-agents.md) +- **systemd:** [deploy/systemd/](../../deploy/systemd/) diff --git a/deploy/compose/README.md b/deploy/compose/README.md index 0de524fb5b..c78dc293d3 100644 --- a/deploy/compose/README.md +++ b/deploy/compose/README.md @@ -54,3 +54,9 @@ $EDITOR .env curl -fsS "http://127.0.0.1:$(grep -E '^BUZZ_HTTP_PORT=' .env | cut -d= -f2-)/_liveness" ./run.sh status ``` + +## Always-on agents (any client) + +- Framework: [docs/self-host-backend-framework.md](../../docs/self-host-backend-framework.md) +- Guide: [docs/self-host-always-on-agents.md](../../docs/self-host-always-on-agents.md) +- Units: [deploy/systemd/](../systemd/) diff --git a/deploy/systemd/README.md b/deploy/systemd/README.md new file mode 100644 index 0000000000..77a9192e16 --- /dev/null +++ b/deploy/systemd/README.md @@ -0,0 +1,33 @@ +# systemd units for always-on Buzz agents + +These units run [`buzz-acp`](../../crates/buzz-acp/README.md) as a supervised +service so agents stay online independent of any Desktop or laptop session. + +See the full framework: [docs/self-host-backend-framework.md](../../docs/self-host-backend-framework.md). + +## Files + +| File | Purpose | +|------|---------| +| `buzz-acp@.service` | Template unit (`buzz-acp@coder`) | +| `buzz-acp.env.example` | Environment skeleton (copy per agent) | + +## Install + +```bash +sudo cp buzz-acp@.service /etc/systemd/system/ +sudo mkdir -p /etc/buzz-acp +sudo cp buzz-acp.env.example /etc/buzz-acp/coder.env +sudo edit /etc/buzz-acp/coder.env # keys + BUZZ_RELAY_URL +sudo systemctl daemon-reload +sudo systemctl enable --now buzz-acp@coder +sudo systemctl status buzz-acp@coder +``` + +User units: install under `~/.config/systemd/user/` and use `systemctl --user`. + +## Requirements + +- `buzz-acp` installed at `/usr/local/bin/buzz-acp` (or edit `ExecStart`) +- Relay reachable at the **same host string** clients use +- Agent pubkey is a relay/channel member as required by your join policy diff --git a/deploy/systemd/buzz-acp.env.example b/deploy/systemd/buzz-acp.env.example new file mode 100644 index 0000000000..1efc695c2e --- /dev/null +++ b/deploy/systemd/buzz-acp.env.example @@ -0,0 +1,20 @@ +# /etc/buzz-acp/.env +# chmod 600 + +# Agent Nostr identity (hex 64 or nsec1…) +BUZZ_PRIVATE_KEY= + +# MUST match Buzz Desktop relay host string exactly (community selector) +BUZZ_RELAY_URL=ws://buzz.example.com:3000 + +# ACP agent binary + args (comma-separated args for buzz-acp) +BUZZ_ACP_AGENT_COMMAND=goose +# BUZZ_ACP_AGENT_ARGS= + +BUZZ_ACP_SUBSCRIBE=mentions +BUZZ_ACP_RESPOND_TO=anyone +# BUZZ_ACP_DISPLAY_NAME=Coder +# BUZZ_ACP_SYSTEM_PROMPT_FILE=/etc/buzz-acp/coder.prompt.md + +# HOME for the agent process (credentials, cwd defaults) +# HOME=/var/lib/buzz-acp/coder diff --git a/deploy/systemd/buzz-acp@.service b/deploy/systemd/buzz-acp@.service new file mode 100644 index 0000000000..faa9e8f6fe --- /dev/null +++ b/deploy/systemd/buzz-acp@.service @@ -0,0 +1,22 @@ +[Unit] +Description=Buzz ACP agent (%i) +Documentation=https://github.com/block/buzz/blob/main/docs/self-host-always-on-agents.md +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +# Environment file: /etc/buzz-acp/%i.env (or EnvironmentFile= override) +EnvironmentFile=-/etc/buzz-acp/%i.env +EnvironmentFile=-/etc/buzz-acp/%i +# Binary on PATH after `cargo install` / release build install +ExecStart=/usr/local/bin/buzz-acp +Restart=on-failure +RestartSec=3 +# Harden lightly β€” agents need homedir tools; tighten further per site +NoNewPrivileges=true +# Limit runaway forks; raise if your agent spawns heavy tool trees +TasksMax=4096 + +[Install] +WantedBy=multi-user.target diff --git a/docs/self-host-always-on-agents.md b/docs/self-host-always-on-agents.md new file mode 100644 index 0000000000..0c4d26eff0 --- /dev/null +++ b/docs/self-host-always-on-agents.md @@ -0,0 +1,172 @@ +# Self-host: always-on agents (any client β†’ server backend) + +Run **agents and the relay on a machine that stays on**. Connect from **any** +Buzz client β€” Desktop (macOS / Windows / Linux), web, mobile, or `buzz` CLI β€” +without tying agent lifetime to a single person’s laptop. + +**Full framework (architecture, security, runbook, topologies):** +[self-host-backend-framework.md](./self-host-backend-framework.md). + +This is the thin-client / remote-backend layout (same idea as pointing a desktop +app at a remote server process). + +| Role | Runs | Examples | +|------|------|----------| +| **Clients** (many) | Human UI only | Desktop on a laptop or workstation, web UI, mobile, `buzz` CLI, CI bots that are *not* coding agents | +| **Backend** (one or more hosts) | `buzz-relay` + `buzz-acp` (+ Postgres/Redis per deploy) | VPS, bare metal, homelab, k8s node | + +Agents stay online when clients disconnect, sleep, or switch devices. + +``` + Desktop ─┐ + Web ─┼── ws(s)://HOST:3000 ──► buzz-relay + Mobile ── β–² + CLI β”€β”˜ buzz-acp @ agent + β”‚ + ACP stdio agent + (goose, claude, codex, + cursor-agent, grok, …) +``` + +Anyone on the community can @mention server-side agents. You do **not** need a +laptop specifically β€” only a client that speaks the relay URL. + +## Why the URL string matters + +Buzz selects the **community** from the connection **Host** header +(`req.community = resolve_host(connection.host)`). + +Use the **same host string** on every client **and** every `buzz-acp`: + +| Good | Bad | +|------|-----| +| `ws://buzz.example.com:3000` everywhere | One device uses `localhost`, agents use the public hostname | +| `ws://myserver.tailnet.ts.net:3000` consistently | Mixing Tailscale DNS and LAN IP for β€œthe same” deploy | +| `wss://buzz.example.com` behind TLS terminator | Clients on `https://…` / `wss://…` while agents still on plain `ws://127.0.0.1` without a matching host map | + +If a client shows empty rooms while the server CLI sees agents, you are almost +always in a **different community** (wrong host) or on a **fresh Nostr identity** +that is not a channel member. + +## 1. Backend: relay + +Follow the main deploy guide (`deploy/compose` or your preferred install) so +`GET http://HOST:3000/_liveness` returns ok and WebSocket `ws://HOST:3000` +(or `wss://…`) accepts clients. + +Ensure the relay is reachable from every client network path you care about +(public DNS, Tailscale, LAN, reverse proxy with WebSocket support). Bind and +firewall so remote clients are intentional, not accidental open internet +without TLS/auth policy. + +## 2. Backend: always-on agent with systemd + +Install `buzz-acp` and `buzz` (CLI) from this repo +(`cargo build --release -p buzz-acp -p buzz-cli`). + +Per agent: + +1. Generate a key: `buzz-admin generate-key` (save the secret). +2. Add the agent as a relay member (closed-relay deploys). +3. Install the unit template from `deploy/systemd/`: + +```bash +# system-wide example +sudo cp deploy/systemd/buzz-acp@.service /etc/systemd/system/ +sudo mkdir -p /etc/buzz-acp +sudo cp deploy/systemd/buzz-acp.env.example /etc/buzz-acp/coder.env +# edit coder.env β€” BUZZ_PRIVATE_KEY, BUZZ_RELAY_URL, agent command +sudo systemctl daemon-reload +sudo systemctl enable --now buzz-acp@coder +sudo systemctl status buzz-acp@coder +``` + +User-level units work the same under `~/.config/systemd/user/` with paths adjusted. + +### Minimal env + +```bash +BUZZ_PRIVATE_KEY= +BUZZ_RELAY_URL=ws://HOST:3000 # MUST match every client host string +BUZZ_ACP_AGENT_COMMAND=goose # or claude-agent-acp, etc. +# BUZZ_ACP_AGENT_ARGS= # comma-separated if needed +BUZZ_ACP_SUBSCRIBE=mentions +BUZZ_ACP_RESPOND_TO=anyone # or owner-only / allowlist +``` + +See [crates/buzz-acp/README.md](../crates/buzz-acp/README.md) for goose, Codex, +Claude Code, multi-agent pools, and heartbeats. + +## 3. Clients: anyone joins the same community + +### Buzz Desktop (any OS) + +1. Install [Buzz Desktop](https://github.com/block/buzz/releases) on the machine + people actually sit at (laptop, desktop workstation, etc.). +2. Use a Nostr identity that is a **member** of the channels you care about + (a random new key will not see existing private membership). +3. Connect / join with the **same** relay URL the agents use: + + `ws://HOST:3000` or `wss://HOST` + +4. Confirm each agent is a **channel member** (same as adding a person). +5. `@mention` the agent. + +Optional env (builds that honor it): + +```bash +export BUZZ_RELAY_URL=ws://HOST:3000 +export BUZZ_AUTO_CONNECT_DEFAULT_RELAY=1 +buzz-desktop +``` + +### Web / mobile + +Point the client at the same community URL. Identity and membership rules are +unchanged: same host string, keys that are members of the right channels. + +### CLI / automation + +```bash +export BUZZ_PRIVATE_KEY= +export BUZZ_RELAY_URL=http://HOST:3000 # HTTP origin for REST; host must map to same community +buzz channels list +buzz messages send --channel --content '@AgentName hello' +``` + +## 4. Smoke test (from any machine that can reach the relay) + +```bash +export BUZZ_PRIVATE_KEY= +export BUZZ_RELAY_URL=http://HOST:3000 +buzz messages send --channel --content '@AgentName ping β€” reply PONG' +buzz messages get --channel --limit 10 +``` + +## Who this is for + +| Audience | Use | +|----------|-----| +| Solo operator | Agents on a VPS; Desktop or CLI at home | +| Small team | Shared community URL; each human brings their own client | +| Homelab | Relay + agents on NAS/mini-PC; phones + laptops join via Tailscale | +| CI / ops | CLI and agents on servers; humans on Desktop/web | + +Not laptop-specific. Laptop is only the common *example* of a client that should +not host production agents. + +## Troubleshooting + +| Symptom | Check | +|---------|--------| +| Empty channels on one device | That device’s host string β‰  agent `BUZZ_RELAY_URL`; or new key | +| Agent never replies | `systemctl status buzz-acp@…`; agent member of channel; subscribe=mentions | +| Works on server CLI only | Other clients connected to different host/community | +| Closed relay rejects a client | Add that client’s pubkey as relay member; improve join/invite UX upstream | +| Voice huddle mic denied | OS microphone permission on the **device running the UI**; remote desktop/noVNC often has no real mic | + +## Related + +- [buzz-acp README](../crates/buzz-acp/README.md) +- [ARCHITECTURE.md](../ARCHITECTURE.md) β€” host β†’ community binding +- Closed-relay client join: track upstream issues on membership / owner bootstrap diff --git a/docs/self-host-backend-framework.md b/docs/self-host-backend-framework.md new file mode 100644 index 0000000000..2a9f3e279e --- /dev/null +++ b/docs/self-host-backend-framework.md @@ -0,0 +1,324 @@ +# Self-host backend framework + +This document is the **full framework** for running Buzz as a real backend: +always-on agents, any client, correct community binding, and layered security. +It is the operator-facing companion to the crate READMEs and compose guides. + +| Doc | Scope | +|-----|--------| +| **This file** | End-to-end framework (read first) | +| [self-host-always-on-agents.md](./self-host-always-on-agents.md) | Step-by-step always-on agents | +| [multi-tenant-relay.md](./multi-tenant-relay.md) | Host β†’ community isolation | +| [ARCHITECTURE.md](../ARCHITECTURE.md) | System architecture | +| [crates/buzz-acp/README.md](../crates/buzz-acp/README.md) | ACP harness reference | +| [deploy/systemd/](../deploy/systemd/) | Unit templates | + +--- + +## 1. Goals + +1. **Anyone can connect** β€” Desktop (any OS), web, mobile, or CLI β€” not a single laptop. +2. **Agents outlive clients** β€” coding agents run on a host that stays up (`buzz-acp`), not inside a sleeping laptop process. +3. **One community, one host string** β€” every client and every agent use the same relay URL host (community selector). +4. **Safer by default** β€” membership + closed-relay rules; optional **shared unlock secret** for operator join packs (dual credential). +5. **Copy-paste deploy** β€” systemd units, env examples, smoke checks. + +Non-goals: replacing `buzz-relay`, inventing a parallel protocol, or requiring a vendor-specific desktop fork. + +--- + +## 2. Layered architecture + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ CLIENTS (thin) β”‚ +β”‚ Desktop Β· Web Β· Mobile Β· buzz CLI Β· automation β”‚ +β”‚ β€” UI + identity only; no requirement to host agents β€” β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ ws(s)://HOST[:port] + β”‚ Host header β‡’ community +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ RELAY (truth) β”‚ +β”‚ buzz-relay Β· Postgres Β· Redis Β· (optional search/object) β”‚ +β”‚ Channels Β· members Β· events Β· invites Β· NIP-42 auth β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ same HOST string +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ AGENT RUNTIME (always-on) β”‚ +β”‚ buzz-acp Γ— N β†’ ACP stdio agents (goose, claude, codex, …) β”‚ +β”‚ systemd Β· env files Β· mentions subscribe Β· channel members β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +| Layer | Responsibility | Binary / surface | +|-------|----------------|------------------| +| Client | Human UX, local mic/camera, display | Desktop / web / mobile / CLI | +| Relay | Durable community state, fan-out, auth | `buzz-relay` | +| Agent harness | Mention β†’ session β†’ tools β†’ reply | `buzz-acp` | +| Agent program | Model + tools (BYOH) | goose, Claude Code ACP, Codex, … | +| Operator pack (optional) | Doctor, profiles, join kit, unlock key | Site-specific tooling or docs only | + +**Execution boundary:** agent tools and files run on the **machine running `buzz-acp`**, not on the device showing the UI. That is intentional and matches production self-host practice. + +--- + +## 3. Community binding (host string) + +Buzz resolves the community from the connection **Host** (see multi-tenant docs): + +```text +req.community = resolve_host(connection.host) +``` + +| Consistent (good) | Split-brain (bad) | +|-------------------|-------------------| +| All agents + all clients: `ws://buzz.example.com:3000` | Desktop: `localhost` Β· agents: `buzz.example.com` | +| All on `wss://buzz.example.com` behind TLS | Mix of `127.0.0.1`, LAN IP, and Tailscale DNS for β€œthe same” deploy | + +**Symptom of mismatch:** empty rooms on one device; full rooms on another; agents β€œonline” but never visible. + +**Rule:** pick one canonical `HOST` (DNS or stable Tailscale name). Put it in every `BUZZ_RELAY_URL` and every client join URL. + +--- + +## 4. Identity and security model + +### 4.1 Protocol identity (required) + +- Every human and agent has a **Nostr keypair**. +- Relay membership and channel membership gate who can read/write. +- Closed-relay deploys: unknown pubkeys cannot join without invite/admin add-member flows. + +### 4.2 Always-on agent identity + +- Each `buzz-acp` instance has its **own** key (not the human’s). +- Agent pubkey must be a **channel member** (and relay member when closed). +- Prefer `BUZZ_ACP_SUBSCRIBE=mentions` so agents reply when addressed. + +### 4.3 Operator dual-lock (recommended) + +In addition to Nostr, operators may require a **shared unlock secret** for backend join packs (same idea as a desktop session token on a remote gateway): + +| Credential | Metaphor | Purpose | +|------------|----------|---------| +| Shared unlock secret | House key | Proves the client was issued access to *this* backend pack | +| Nostr key | Name badge | Proves *who* is acting; drives membership | + +**Not automatic trust:** possession of a leaked channel member key alone should not be treated as full operator onboarding. Issue join material that includes: + +1. Canonical `BUZZ_RELAY_URL` (host string) +2. Human or device Nostr secret (or instruct generate-then-add-member) +3. Optional `BUZZ_BACKEND_UNLOCK_KEY` (shared with server operator store) +4. Fingerprint of unlock for out-of-band verify without pasting the secret in chat + +Store unlock material **0600**, never commit, never paste into public issues. + +Relay protocol auth remains Nostr; dual-lock is an **operator onboarding and tooling** control unless you also terminate TLS/auth at a reverse proxy. + +### 4.4 Invites + +Official relay supports invite mint/claim (`/api/invites`, `/invite/...`). Prefer invites for new humans on closed relays instead of copying a single shared nsec across a team. + +--- + +## 5. Always-on agents + +### 5.1 Process model + +- One or more `buzz-acp` processes, supervised by **systemd** (or equivalent). +- Each process: unique key, unique display name, one ACP command line. +- Relay URL **identical** to clients. + +### 5.2 systemd + +Templates live in [`deploy/systemd/`](../deploy/systemd/): + +- `buzz-acp@.service` β€” instance unit (`buzz-acp@coder`) +- `buzz-acp.env.example` β€” env skeleton + +```bash +sudo cp deploy/systemd/buzz-acp@.service /etc/systemd/system/ +sudo mkdir -p /etc/buzz-acp +sudo cp deploy/systemd/buzz-acp.env.example /etc/buzz-acp/coder.env +# edit keys + BUZZ_RELAY_URL=ws://HOST:3000 +sudo systemctl daemon-reload +sudo systemctl enable --now buzz-acp@coder +``` + +### 5.3 Minimal env + +```bash +BUZZ_PRIVATE_KEY= +BUZZ_RELAY_URL=ws://HOST:3000 +BUZZ_ACP_AGENT_COMMAND=goose +BUZZ_ACP_SUBSCRIBE=mentions +BUZZ_ACP_RESPOND_TO=anyone # or tighter +``` + +See [buzz-acp README](../crates/buzz-acp/README.md) for pools, heartbeats, allowlists, and editor agents. + +### 5.4 Channel membership + +Agents are participants. After key generation: + +1. Add relay member (closed mode). +2. Create or select channels. +3. `add-member` agent pubkey to each channel they should serve. +4. Smoke with `@AgentName` from any client on the same host string. + +--- + +## 6. Clients (any device) + +### 6.1 Desktop + +1. Install from [releases](https://github.com/block/buzz/releases). +2. Import or create identity that is a **member**. +3. Connect to `ws://HOST:3000` or `wss://HOST` β€” **same host as agents**. +4. Optional env (when supported by the build): + +```bash +export BUZZ_RELAY_URL=ws://HOST:3000 +export BUZZ_AUTO_CONNECT_DEFAULT_RELAY=1 +``` + +### 6.2 Web / mobile + +Same community URL and membership rules. No special laptop requirement. + +### 6.3 CLI + +```bash +export BUZZ_PRIVATE_KEY= +export BUZZ_RELAY_URL=http://HOST:3000 +buzz channels list +buzz messages send --channel --content '@Agent hello' +``` + +### 6.4 Join kit (operator checklist) + +Hand a private file (or secure channel) containing: + +```text +BUZZ_RELAY_URL=ws://HOST:3000 +BUZZ_COMMUNITY_HOST=HOST:3000 +# identity +NSEC=... # or BUZZ_PRIVATE_KEY= +# optional dual-lock +BUZZ_BACKEND_UNLOCK_KEY=... +BUZZ_UNLOCK_FP= +``` + +Client onboarding: import identity β†’ set relay URL β†’ confirm unlock fingerprint with operator if used β†’ open channel β†’ mention agent. + +--- + +## 7. Health, probe, readiness + +Before calling a deploy β€œup,” check: + +| Check | Expect | +|-------|--------| +| `GET http://HOST:3000/_liveness` | ok | +| WebSocket upgrade to same host | HTTP 101 | +| `systemctl status buzz-acp@…` | active | +| Mention smoke | Agent reply in channel | +| Host string | Identical on sample client env and agent env | + +**Readiness idea (operators):** after fleet start, record a line such as: + +```text +BUZZ_BACKEND_READY acp=N relay=ws://HOST:3000 +``` + +Do not treat β€œbinary exists” as healthy without liveness + WS + at least one successful agent path. + +--- + +## 8. Multi-channel work layout (optional pattern) + +Communities often split work by channel rather than one mega-room: + +| Channel (example) | Purpose | +|-------------------|---------| +| `orchestrator` | Assignments / priorities | +| `eng-build` | Implementation | +| `eng-review` | Safety / ship gate | +| `eng-ops` | Infra / relay / agents | +| `clients` | Customer-named work | +| … | Team-specific | + +Agents may be members of many channels; humans join the rooms they need. This is organizational, not a protocol requirement. + +--- + +## 9. Deploy topologies + +| Topology | When | +|----------|------| +| Single VPS | Relay + Postgres + Redis + all `buzz-acp` on one host | +| Split | Relay/data on A; agent fleet on B with tools/GPU; same public HOST via proxy | +| Homelab + tailnet | `HOST` = MagicDNS name; clients on phones/laptops via Tailscale | +| TLS edge | `wss://HOST` terminated at Caddy/nginx; agents still use the **public host string** that clients use | + +Bind and firewall deliberately. Prefer Tailscale or private network when not ready for public internet. + +--- + +## 10. Operator runbook (short) + +```text +1. Install relay (compose or binary) β†’ liveness ok +2. Choose canonical HOST string +3. Create human identity; ensure relay/channel membership +4. For each agent: key β†’ member β†’ buzz-acp systemd β†’ channel member +5. Issue join kit to each human device (URL + identity [+ unlock]) +6. From any client: open channel, @mention agent +7. On failure: compare Host strings; check membership; journalctl -u buzz-acp@… +``` + +--- + +## 11. Troubleshooting matrix + +| Symptom | Likely cause | +|---------|----------------| +| Empty channels on one device | Different host string or new Nostr key | +| Agent never replies | Not in channel; acp down; subscribe/filter; model auth | +| Works on server CLI only | Clients not on same community URL | +| Closed relay rejects client | Need invite or admin add-member | +| Voice mic denied | OS permission on the **UI device**; remote desktop often has no mic | +| β€œI ran grok in tmux” but hive silent | Local CLI β‰  `buzz-acp` seat on the server | + +--- + +## 12. Framework summary + +```text +CLIENTS (any) --same HOST--> RELAY <--same HOST-- ACP AGENTS (always-on) + Nostr membership + (+ optional operator unlock on join packs) +``` + +| Principle | Practice | +|-----------|----------| +| Thin clients | UI devices do not host production agents | +| Always-on agents | systemd + buzz-acp on a stable host | +| One community | One host string everywhere | +| Defense in depth | Membership + invites + optional shared unlock | +| Verify | Liveness, WS, mention smoke | + +--- + +## 13. Related code and deploy paths + +| Path | Role | +|------|------| +| `crates/buzz-relay` | Relay | +| `crates/buzz-acp` | Agent harness | +| `crates/buzz-cli` | Operator/agent CLI | +| `deploy/compose` | Container deploy | +| `deploy/systemd` | Always-on agent units | +| `desktop/` | Official Desktop client | + +Contributions that extend this framework (Desktop connection UX for host mismatch warnings, join-kit import, unlock field, closed-relay owner bootstrap) should link back here so operators keep a single mental model.