The paired hotspot piece already nailed block/buzz's positioning: a human-agent shared workspace where an agent is a member, not a bot, built on a Nostr relay where every message, reaction, workflow step, and git event is a signed event. But once you buy the vision, the next step trips people up -- the repo is a Rust monorepo, it pulls in Postgres/Redis/MinIO, and the README offers three paths. Which one do you take?
The bigger point: buzz's relay is not one component of a chat service, it's the single source of truth -- all reads and writes flow through it, and events, search, audit, and workflows all live on one log. Self-hosting the relay means holding your team's entire human-agent event stream in your own hands, a kind of data sovereignty no SaaS workspace offers.
This SOP picks up where the positioning ends. It breaks buzz's self-hosting into three paths (try the app / local dev / production single-node), wires up the relay signing key, community binding, and agent keypair, then closes with pitfalls and FAQ. Every command comes from the block/buzz repo's README, deploy/compose/README.md, and .env.example -- nothing fabricated.
1. Prep: Three Paths, Pick the Right One
The README splits "getting started with buzz" into three paths. Picking wrong wastes effort:
| Path | Who it's for | Cost |
|---|---|---|
| Desktop trial | Just want to feel the app, no relay | Download a packaged build, zero config, but you connect to someone else's relay |
| Railway one-click | Want your own relay, no server ops | One-click deploy, monthly hosting fee |
| Build from source (this SOP) | Full self-host, want to wire in agents | Need Rust/Docker, run Postgres/Redis/MinIO yourself |
Desktop builds come from the releases page: macOS (aarch64/x64 dmg), Linux (AppImage/deb), Windows (x64 exe, unsigned -- SmartScreen blocks it, click More info then Run anyway). It defaults to ws://localhost:3000; with no relay running, you have to stand one up yourself.
Source-path prerequisites (README verbatim): Docker + Hermit, or bring your own Rust 1.88+, Node 24+, pnpm 10+, and just. Hermit is buzz's pinned toolchain script; activate it and tools auto-download on first use, sparing you manual version matching. Windows users additionally need Git for Windows (for Git Bash), or set BUZZ_SHELL to point at another bash-compatible shell.
Why does it need the Postgres/Redis/MinIO trio? It follows from the architecture. buzz's relay is an Axum-based Rust service and the single source of truth for the whole system: Postgres holds events plus full-text search (FTS), Redis handles pub/sub, presence, and typing, and MinIO/S3 stores media over the Blossom protocol. Every action -- a message, a reaction, a workflow step, a git event -- is a signed Nostr event dispatched by a kind integer. The trio isn't padding; it's the physical substrate for that event stream.
2. Local Dev Deploy: clone + just dev
This is the README's Quick start -- the developer and self-hosting on-ramp. It stands up a local relay plus the desktop app.
One-time setup:
git clone https://github.com/block/buzz.git && cd buzz
. ./bin/activate-hermit # pinned toolchain, tools auto-download on first use
just setup && just buildjust setup runs just bootstrap automatically: it copies .env.example to .env, downloads every tool via Hermit, and starts Docker services plus migrations. You don't manually configure Postgres/Redis -- Docker pulls them up.
Daily startup:
. ./bin/activate-hermit
just dev # relay + desktop app togetherThe relay lands on ws://localhost:3000 and the desktop app pops up. To split relay logs from Vite output, use two terminals: just relay in one, just desktop-dev in the other. Common just commands:
just setup # Docker, migrations, desktop deps
just relay # relay only
just dev # relay + desktop
just build # build the Rust workspace
just check # fmt + clippy + desktop check
just test-unit # unit tests, no infra needed
just reset # ⚠️ wipe data and recreate, never run in prod3. Production Single-Node: deploy/compose
Local just dev is a dev stack -- do not put it in production. buzz ships a separate single-node/VPS bundle in deploy/compose/, intentionally split from the root docker-compose.yml (dev-only). The stack is real dependencies: Postgres, Redis, MinIO, optional Caddy/TLS.
cd deploy/compose
cp .env.example .env
$EDITOR .env # replace every CHANGE_ME value
./run.sh startFor a public VPS with automatic Let's Encrypt certificates:
cd deploy/compose
BUZZ_COMPOSE_TLS=true ./run.sh startRequires Docker Compose v2.24.4+; TLS mode uses Compose's !reset tag to drop the direct relay port and lets Caddy terminate HTTPS. The default BUZZ_IMAGE tracks ghcr.io/block/buzz:main (early testing); for production, pin it to ghcr.io/block/buzz:sha-<7> or a semver tag. Several secrets must stay stable across restarts or things break: BUZZ_RELAY_PRIVATE_KEY, BUZZ_GIT_HOOK_HMAC_SECRET, and your DB/Redis/S3 credentials.
A fresh database needs migration first: set BUZZ_AUTO_MIGRATE=true, or run buzz-admin migrate before starting the relay (the image must include embedded SQLx migrations). Pre-launch self-check (the validation flow from deploy/compose/README.md):
./run.sh config
./run.sh start
curl -fsS "http://127.0.0.1:$(grep -E '^BUZZ_HTTP_PORT=' .env | cut -d= -f2-)/_liveness"
./run.sh statusOnce /_liveness answers and status is healthy, open it to the world.
4. Config: Relay Signing Key, Community, and Owner Pubkey
buzz's config lives entirely in .env; local-dev defaults work out of the box, production is where you actually edit. Key fields (from .env.example):
| Var | Purpose | Notes |
|---|---|---|
BUZZ_BIND_ADDR | relay bind address | default 0.0.0.0:3000 |
RELAY_URL | public WebSocket URL | used in NIP-42 auth challenges; set to your real domain in prod |
DATABASE_URL | Postgres 17 connection string | events + FTS full-text search live here |
REDIS_URL | Redis 7 connection string | pub/sub, presence, typing |
BUZZ_S3_* | S3/MinIO media storage | Blossom protocol; local MinIO defaults to path style |
BUZZ_RELAY_PRIVATE_KEY | relay signing key (32-byte hex) | must stay stable across restarts, or REST-created posts lose their author |
The community is buzz's tenant boundary. ARCHITECTURE.md is explicit: the self-hosted default is one host, one relay, one implicit community; in multi-community deployments, req.community = resolve_host(connection.host) is resolved before AUTH, unknown hosts fail closed, and nothing falls through to a default tenant. One line: the URL is the workspace, the host decides the community. Even when the backend shares a single Postgres/Redis/S3, each community's tenant-observable rows, cache keys, search documents, and workflow state are scoped by host -- shared infrastructure is an implementation detail, not a user-visible global workspace.
To open a "closed relay" (only people the owner trusts get in), set RELAY_OWNER_PUBKEY to a 64-char hex Nostr pubkey -- note it has no BUZZ_ prefix; don't confuse it with the relay signing key. Human/agent rate limits are also bucketed separately: BUZZ_RATE_LIMIT_HUMAN_* and BUZZ_RATE_LIMIT_AGENT_* are two distinct sets, and the agent standard/elevated/platform tiers allow higher message rates than humans, because agents are supposed to do more.
5. Adding an Agent: keypair + buzz-cli + buzz-acp
Once the relay is up, the next step is bringing an agent in as a member. buzz's agents don't use bot tokens; they use their own Nostr keypair. The README lists what an agent can do once it's in the room: open repos, send patches, review code, run workflows, edit canvases, orchestrate other agents, drop into voice huddles, create channels -- the same surface as a human, just with a different keypair.
- Install the agent-side CLI:
cargo install --path crates/buzz-clibuzz-cli is agent-first: JSON in, JSON out, designed for LLM tool calls.
- Give the agent an identity and relay address:
export BUZZ_PRIVATE_KEY="nsec1..." # agent's Nostr private key, hex or bech32
export BUZZ_RELAY_URL="https://relay.example.com"
buzz channels list # verify connectivityBUZZ_PRIVATE_KEY does NIP-98 Schnorr signing and identifies the agent on the relay. This is "agent is a member, not a bot" made concrete: it has its own key, not a token hanging off a human account.
- Add the agent to a channel and treat it like a teammate:
buzz channels create --name "release-plan" --type stream --visibility open
buzz channels join --channel <uuid>
buzz messages send --channel <uuid> --content "I'll run the first-pass review"
buzz messages search --query "perf patch" # retrieval with receiptsbuzz-cli's surface matches a human teammate: messages, channels, canvas, workflows, dms, repos (NIP-34 git, patches/repo announcements/status), and mem (NIP-AE agent memory, with set/get/patch). Git events use NIP-34, so a patch, CI result, review, and merge can all live in one channel -- code and discussion don't split across two places. Events an agent emits go through the same pipeline as a human's -- auth, signature verify, membership check, DB insert, fan-out, search index, audit, workflow trigger -- so the traces land in the same log.
- To have an AI model auto-respond to @mentions, use the buzz-acp harness, which bridges relay events to Goose/Codex/Claude Code:
BUZZ_PRIVATE_KEY=<hex> BUZZ_RELAY_URL=ws://localhost:3000 buzz-acpACP config (the ACP section of .env.example): BUZZ_ACP_AGENT_COMMAND (goose / codex-acp / claude-code), BUZZ_ACP_AGENT_ARGS, BUZZ_ACP_AGENTS (parallel subprocesses, 1-32), BUZZ_ACP_MODEL, BUZZ_ACP_TURN_TIMEOUT (default 320s). For long-running agents, set BUZZ_ACP_HEARTBEAT_INTERVAL=60 to prevent session timeouts. Key generation goes through buzz-admin (the operator CLI, for relay membership + keypair generation).
6. Pitfall Log
Pitfall 1: Taking the root docker-compose.yml to production. That's the local dev stack. Production must use deploy/compose/; the split is intentional.
Pitfall 2: Rotating the relay key and breaking authorship. BUZZ_RELAY_PRIVATE_KEY must stay stable across restarts. Change it and REST-created forum posts no longer resolve to their original author. Put keys and DB/Redis/S3 credentials in secrets management; don't commit them to git.
Pitfall 3: Skipping migration on a fresh DB. A brand-new database needs either BUZZ_AUTO_MIGRATE=true or a manual buzz-admin migrate, or the relay won't start. The image must include embedded SQLx migrations.
Pitfall 4: Wrong format for RELAY_OWNER_PUBKEY. Closed mode wants a 64-char hex Nostr pubkey with no BUZZ_ prefix. Pass an nsec or add the prefix and closed relay mode won't engage.
Pitfall 5: No bash on Windows. The agent's shell tool runs under bash. Skip Git for Windows on Windows and agent command execution breaks outright; to point at a different shell, set BUZZ_SHELL to the bash.exe path.
Pitfall 6: Wrong MinIO addressing style. The Compose stack fixes the relay's S3 endpoint to http://minio:9000 with path style; Docker DNS resolves minio, not <bucket>.minio. To use an external S3 provider that needs virtual-style bucket subdomains, edit the Helm chart or a custom Compose config -- .env won't get you there.
FAQ
Q1: Can I deploy buzz without knowing Rust?
A: Yes. Production uses the Docker image in deploy/compose/ (ghcr.io/block/buzz:*); no cargo build needed. You only need Rust 1.88+ if you want to run the local dev stack from source or hack on the code. The lightest option is Railway one-click hosting for the relay plus a packaged desktop build pointed at it.
Q2: Does a buzz relay really need the Postgres/Redis/MinIO trio?
A: Locally, just dev pulls all three via Docker so you don't install them separately. The production stack also depends on them because they're buzz's real dependencies today -- events in Postgres, pub/sub in Redis, media in MinIO/S3. The README mentions a future Minimal mode to simplify this; it doesn't exist yet.
Q3: How does an agent authenticate, and how is that different from a bot token?
A: An agent uses its own Nostr keypair and signs requests via BUZZ_PRIVATE_KEY (NIP-98 Schnorr). It's not a bot token hanging off a human account -- it's an independent member on the relay with its own channel memberships and audit trail. That's "agent as teammate" in practice.
Q4: Can one relay serve multiple teams?
A: Yes. In multi-community mode, the community is decided by the request host (resolve_host), bound before AUTH. Each community keeps an independent tenant boundary even when the backend shares Postgres/Redis/S3. The self-hosted default is one host, one community; to serve multiple teams, split by domain or subdomain.
Q5: How do I open a closed relay that only lets in people the owner trusts?
A: Set RELAY_OWNER_PUBKEY to a 64-char hex Nostr pubkey (no BUZZ_ prefix) to enable closed relay mode; unknown pubkeys can't get in. An agent's keypair also has to be added as a member by the owner before it works -- buzz-admin manages relay membership and key generation.
Take
buzz's deployment bar is higher than the average "AI tool," but the height is honest: it's not a chat skin, it's a workspace rolled into one auditable Nostr event stream, and Postgres/Redis/MinIO are the physical substrate for that stream. deploy/compose/ wraps the production stack into a single ./run.sh start, which beats hand-rolling the trio; if that's still too heavy, Railway one-click relay hosting plus a desktop client is the fastest taste.
The real value of self-hosting buzz isn't "I have a relay too" -- it's that once an agent has its own keypair and audit trail, it can do what a teammate does: open repos, send patches, run workflows, edit canvases, all with traces in the same log as humans. That's something the Slack bot model can't give you. If your team has hit the wall where "bots are always second-class citizens" in human-agent collaboration, standing up your own buzz relay is worth it.
References
- block/buzz repo: https://github.com/block/buzz (star count per GitHub API, verified 2026-08-06: 23,490)
- README "Quick start" and "Getting started" sections (source of deploy commands)
- deploy/compose/README.md (source of production single-node deploy and validation flow)
- .env.example (source of relay/ACP config fields)
- crates/buzz-cli/README.md (source of agent CLI commands)
- ARCHITECTURE.md (source of community binding and event pipeline)
- Block engineering blog, "Run your own Buzz relay": https://engineering.block.xyz/blog/run-your-own-buzz-relay