Field SOP
Field SOP

Self-Hosting block/buzz: A Deployment SOP from Docker to Agent Onboarding

A full self-hosting SOP for block/buzz (paired with the buzz-hive-mind hotspot piece): local dev stack (just setup/build/dev) plus production single-node (deploy/compose Docker, Postgres/Redis/MinIO) plus configuration (.env: RELAY_URL/BUZZ_RELAY_PRIVATE_KEY/RELAY_OWNER_PUBKEY) plus agent onboarding (Nostr keypair NIP-98 signing, buzz-admin manages members) plus closed relay plus 5 FAQ. All deployment commands are sourced from README/compose/.env/CLI/ARCHITECTURE, nothing fabricated.

Published August 6, 20269 min read
<!-- buzz-deploy-sop | sop | Self-Hosting a Buzz Relay SOP: Build a Human-Agent Shared Workspace for Your Team -->

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:

PathWho it's forCost
Desktop trialJust want to feel the app, no relayDownload a packaged build, zero config, but you connect to someone else's relay
Railway one-clickWant your own relay, no server opsOne-click deploy, monthly hosting fee
Build from source (this SOP)Full self-host, want to wire in agentsNeed 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:

bash
git clone https://github.com/block/buzz.git && cd buzz
. ./bin/activate-hermit   # pinned toolchain, tools auto-download on first use
just setup && just build

just 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:

bash
. ./bin/activate-hermit
just dev   # relay + desktop app together

The 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:

bash
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 prod

3. 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.

bash
cd deploy/compose
cp .env.example .env
$EDITOR .env       # replace every CHANGE_ME value
./run.sh start

For a public VPS with automatic Let's Encrypt certificates:

bash
cd deploy/compose
BUZZ_COMPOSE_TLS=true ./run.sh start

Requires 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):

bash
./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 status

Once /_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):

VarPurposeNotes
BUZZ_BIND_ADDRrelay bind addressdefault 0.0.0.0:3000
RELAY_URLpublic WebSocket URLused in NIP-42 auth challenges; set to your real domain in prod
DATABASE_URLPostgres 17 connection stringevents + FTS full-text search live here
REDIS_URLRedis 7 connection stringpub/sub, presence, typing
BUZZ_S3_*S3/MinIO media storageBlossom protocol; local MinIO defaults to path style
BUZZ_RELAY_PRIVATE_KEYrelay 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.

  1. Install the agent-side CLI:
bash
cargo install --path crates/buzz-cli

buzz-cli is agent-first: JSON in, JSON out, designed for LLM tool calls.

  1. Give the agent an identity and relay address:
bash
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 connectivity

BUZZ_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.

  1. Add the agent to a channel and treat it like a teammate:
bash
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 receipts

buzz-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.

  1. To have an AI model auto-respond to @mentions, use the buzz-acp harness, which bridges relay events to Goose/Codex/Claude Code:
bash
BUZZ_PRIVATE_KEY=<hex> BUZZ_RELAY_URL=ws://localhost:3000 buzz-acp

ACP 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

This article is AI-assisted and human-edited. Last updated: 2026-08-06

FAQ

Can I deploy buzz without knowing Rust?
For production, use the Docker image in deploy/compose/ (ghcr.io/block/buzz:*), no cargo build needed. You only need Rust 1.88+ to run the local dev stack or modify code. The easiest path is Railway one-click hosting the relay plus the desktop client.
Must the relay run Postgres/Redis/MinIO?
Local just dev pulls them all via Docker; the production stack also depends on them, which are the real current dependencies of buzz (events in Postgres, pub/sub via Redis, media via MinIO/S3). README mentions a future Minimal mode, not available today.
How do agents authenticate? How is it different from a bot token?
Agents use their own Nostr keypair; BUZZ_PRIVATE_KEY does NIP-98 Schnorr signing, making the agent an independent member on the relay (with channel membership and audit trail), not a token hung under a human account.
Can one relay serve multiple teams?
Yes. In multi-community mode, the community is decided by the request host (resolve_host), bound before AUTH; when the backend shares Postgres/Redis/S3, tenant-visible data is still isolated by host. Default is one community per machine; multiple teams split by domain or subdomain.
How to enable a closed relay?
Set RELAY_OWNER_PUBKEY to a 64-hex Nostr public key (no BUZZ_ prefix) to enable closed relay mode; agent keypairs also need the owner to add them as members, and buzz-admin manages members and key generation.

Related

Field SOP

Building an AI Agent Workflow in n8n: A Deployment and Pitfall SOP

A full SOP for building a tool-calling AI agent workflow inside the n8n canvas: one-command Docker self-host deployment, AI Agent node four-piece anatomy (Language Model, Memory, Tools, System Prompt), step-by-step build (pick trigger, configure node, add tools, output, test and publish), five pitfalls (amnesia from missing Memory, hardcoded API keys, over-engineering, context drift, data format mismatch) plus 5 FAQ. Node parameters per n8n official docs; gives config logic, no fabricated full JSON.

Aug 6, 20269 min read
Field SOP

AI Digital Human Creation SOP: A Repeatable Workflow from Script to Final Cut

Breaks AI digital human creation into a six-step repeatable workflow: pick the tool by use case (HeyGen/D-ID/Synthesia/Colossyan/DeepBrain plus China's Tencent Zhiying/Guiji Intelligent), write the talking-head script (with prompt template), pick or customize the avatar, lock the voice before driving lip-sync, post-process subtitles/editing/compliance, and publish with platform adaptation. Includes 5 pitfalls (avatar licensing/lip-sync drift/multilingual voice/long-video cost/compliance labels) and 5 FAQs. Representative workflow, not a single-tool hands-on test; features subject to official sites.

Aug 7, 20268 min read