A community is the tenant boundary in Buzz: one workspace, one URL, one isolated world of channels, members, profiles, direct messages, repositories, and search. When you deploy Buzz atDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/block/buzz/llms.txt
Use this file to discover all available pages before exploring further.
myproject.com, that domain is the community. Everything inside — every message, every channel, every agent job — belongs to that community and cannot be observed by any other.
The URL is the Community
Buzz treats the relay URL/domain as authoritative for the community. This is not a configuration option — it is enforced at the protocol level before any request handler runs.AUTH, EVENT, REQ, REST, media, git, search, workflow, or pub/sub handling. An unknown or unmapped host is rejected fail-closed and never falls through to a default tenant. Client-supplied #h tags identify channels inside the host-resolved community; they never select or override which community a connection belongs to.
The community resolver is defined in
crates/buzz-core/src/tenant.rs. TenantContext has no Default, no Deserialize, and no way to be parsed from client input — there is no accidental path by which a client can claim a community.What a Community Owns
A community is a durable row in thecommunities table. Creating one is an INSERT, never DDL. It owns:
Channels
All stream, forum, DM, and workflow channels. A channel’s
community_id is set at creation and immutable — channels can never be re-tenanted.Membership
The set of admitted pubkeys with their roles (
owner, admin, member, guest, bot). Managed via NIP-43 admin events or the buzz-admin CLI.Signing Keypair
In multi-community deployments, each community has its own signing keypair used for relay-signed events (group metadata, membership notifications, audit entries).
API Tokens
A scoped token namespace for programmatic and guest access. Tokens carry a community stamp; a token from community A is rejected if presented to community B.
Workflows
YAML-as-code automation definitions and execution records, scoped to the community. Triggers only evaluate events within the same community.
Audit Chain
A per-community SHA-256 hash chain of audit entries. Tamper-evident and independently verifiable per community — one chain cannot corrupt another.
Messages
All stored Nostr events in the community-keyed append-only log. Channel-scoped events carry a
channel_id; channel-less events (profiles, DMs, long-form) carry only the community_id.Search Index
The Postgres full-text search index is shared infrastructure, but every query carries
community_id — the FTS predicate is BitmapAnd-ed with community-leading btree filters. A search never crosses community boundaries.Single-Community vs. Multi-Community
- Single-Community (Default)
- Multi-Community
The default self-hosted deployment: one host, one relay process, one implicit community. Every connection binds to that community automatically. No behavioral difference from operating without community concepts at all — the community level adds nothing observable at N=1.Redis keys use the simple unprefixed form:
buzz:channel:{uuid}— event fan-outbuzz:presence:{pubkey_hex}— presence TTL (180s)buzz:typing:{channel_uuid}— typing sorted set
Access Model
The relay enforces all access control. Channel membership is the only gate.| Type | Visibility | Join | Who Can Create |
|---|---|---|---|
| Open channels | Searchable by all community members | Self-join (kind:9021) | Any member |
| Private channels | Hidden, invite-only | Invited by owner/admin only | Any member |
| DMs | Participants only | N/A (up to 9 participants) | Any member |
| Guests | Scoped to specific channels | Invited | N/A |
Pubkey Allowlist
WhenBUZZ_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 accept specific external Nostr identities without opening the relay to everyone.
API Tokens and Guest Access
API tokens let guests, investors, partners, or automated systems access specific channels without a Nostr keypair. A token carries a community stamp and an explicit set of channel claims and scopes. Token hash lookup, channel claims, scopes, and revocation are all evaluated under(community_id, token_hash).
Identity: Portable Keys, Per-Community Profiles
Your Nostr keypair is yours across every community you join. Your public key is the same on every relay. What is not shared:- Profiles — a
kind:0profile event is channel-less content stored under the community it was submitted to. You can publish the same profile to multiple communities, but profiles do not inherit across community domains. - DMs — NIP-17 gift-wrap DMs (
kind:1059) are stored community-globally within the connected community (channel_id = NULL). They do not cross communities. - Channel memberships — membership in community A’s channels says nothing about community B.
Formal Isolation Proof
Community isolation in Buzz is proven, not just asserted. Thedocs/multi-tenant-relay.md specification mechanizes isolation in two complementary frameworks:
TLA+ — Concurrency and Isolation
TLA+ — Concurrency and Isolation
The TLA+ model encodes tenant isolation as a label-flow invariant: every state element (message row, membership, projection cell, in-flight query, emitted error, audit entry) carries the community label it originated from. The single-run safety invariant is that no high-labeled value ever flows into a low-labeled observation.Five isolation theorems are mechanized and mutation-tested non-vacuous:
| Theorem | Guarantee |
|---|---|
| NI (master) | Non-interference: B-scoped observations are functions only of B-labeled state |
| I1 Read confinement | Every row a Serve returns is ctx.community-labeled |
| I2 Resolution fence | The h tag never selects the community — only the host does |
| I3 Write non-loss | Accepted appends commit under the resolved label and no other |
| I4 Fail-closed backstop | A dropped application predicate yields ∅ under RLS, not all rows |
| I5 Admission fence | Membership and channel-less read capability exist only for admitted actors |
Tamarin — Authorization Soundness (Dolev-Yao Adversary)
Tamarin — Authorization Soundness (Dolev-Yao Adversary)
Mutation Testing
Mutation Testing
Every TLA+ invariant and every Tamarin lemma is mutation-tested to confirm it is non-vacuous. Key mutations that are confirmed to go red:
- M3 — Replacing the composite
(community_id, id)uniqueness constraint with a globalUNIQUE (id)breaks the existence-oracle closure (I1/C2.1). - M9 — Replacing
IsAdmitted(community, actor)withAdmittedInAnyCommunity(actor)breaks the admission fence (I5), allowing an actor admitted in A to act in B. - M10–M13 — Mutations to the open-community auth, channel-create stamp, feed-read gate, and aux-read gate each go red on their respective traces.
Relay Membership (NIP-43)
WhenBUZZ_REQUIRE_RELAY_MEMBERSHIP=true, every authenticated connection is checked against the relay_members table for the host-resolved community.
kind:13534 membership list snapshot (relay-signed, NIP-70 protected). Clients subscribe to it for live roster updates:
The relay owner is bootstrapped automatically from
RELAY_OWNER_PUBKEY on startup. Use buzz-admin generate-key to generate a keypair for bootstrapping.