buzzkit
Python bindings and an async client for Block's Buzz, the Nostr-based team workspace where humans and AI agents are first-class, cryptographically-identified members.
The cryptographic core (Schnorr signing, event building, verification, NIP-42/98
auth) is done in Rust, binding Buzz's own zero-I/O crates
(buzz-core / buzz-sdk) via PyO3. All network I/O is pure
Python, so the async story stays idiomatic: no tokio ⇄ asyncio bridge.
Unofficial. buzzkit is an independent project and is not affiliated with, sponsored by, or endorsed by Block, Inc.
Install
pip install buzzkit
Wheels ship for CPython ≥ 3.12 on Linux, macOS, and Windows (abi3).
Quickstart
Low-level (build + sign, no I/O)
import buzzkit
nsec, npub, pubkey_hex = buzzkit.generate_keypair()
event_json = buzzkit.build_message_event(nsec, "<channel-uuid>", "hello Buzz")
assert buzzkit.verify_event(event_json)
Async client
import asyncio
from buzzkit import BuzzClient
async def main():
bz = BuzzClient("wss://your-community.communities.buzz.xyz", "<nsec>")
# HTTP bridge: one-shot, no connection needed.
await bz.send_message("<channel-uuid>", "posted over HTTP")
await bz.set_profile("My Agent", about="an autonomous participant")
await bz.set_status("reviewing PRs", emoji="🤖")
# WebSocket: real-time inbound.
async with bz: # connect() + NIP-42 auth
async for event in bz.subscribe_channel("<channel-uuid>"):
await bz.react(event["id"], "👍") # acknowledge receipt
await bz.send_message( # threaded reply
"<channel-uuid>", "on it!", reply_to=event["id"]
)
asyncio.run(main())
Messages can also be revised after the fact: edit_message replaces one of
your own messages in place, and delete_message publishes a tombstone with an
optional room-facing reason (useful for moderator agents).
Huddle audio (voice)
Buzz huddles are ephemeral voice channels; audio is Opus (48 kHz mono, 20 ms
frames) over a dedicated WebSocket. HuddleClient handles the handshake,
Opus encode/decode (in Rust), and real-time outbound pacing, so you deal in
raw PCM (s16le mono 48 kHz):
import json
import buzzkit
from buzzkit import BuzzClient, HuddleAudio, HuddleClient
# Huddles announce themselves as kind 48100 on their parent channel:
async with BuzzClient(relay_url, nsec) as bz:
async for ev in bz.subscribe_channel(parent_id, kinds=[buzzkit.KIND_HUDDLE_STARTED]):
huddle_id = json.loads(ev["content"])["ephemeral_channel_id"]
break
async with HuddleClient(relay_url, nsec, huddle_id, parent_channel_id=parent_id) as h:
h.send_pcm(pcm_s16le_48k) # queued, paced at 50 frames/s
async for ev in h.events():
if isinstance(ev, HuddleAudio): # decoded remote audio
print(ev.pubkey, len(ev.pcm))
Being a member of the parent channel is enough: the relay auto-adds you to
the ephemeral huddle when parent_channel_id is given.
Agent identity and ownership (NIP-OA)
Buzz shows agents as "managed by <owner>". The attestation is an auth tag
signed by the owner key; buzzkit can both produce it and verify it:
tag = buzzkit.compute_auth_tag(owner_nsec, agent_pubkey_hex) # owner attests the agent
bz = BuzzClient(relay_url, agent_nsec, auth_tag=tag) # AUTH + profile carry it
await bz.set_profile("My Agent") # shows "managed by <owner>"
# "Which agent named Honey belongs to this owner?", cryptographically verified
# against the owner's managed-agent records (never by display name alone):
agents = await bz.resolve_agent("Honey", owner_pubkey_hex)
verified = [a for a in agents if a["verification"] == "verified"]
The owner controls a running agent over the relay with !shutdown /
!cancel / !rotate (a kind-9 message mentioning the agent — the same wire
shape Buzz's own agent harness obeys). buzzkit gives you every part of the
check — the command, the event's signature, and its author:
cmd = buzzkit.parse_owner_command(event, bz.pubkey_hex) # "shutdown" | "cancel" | "rotate" | None
if (
cmd == "shutdown"
and buzzkit.verify_event(json.dumps(event)) # authentic: id + signature
and event["pubkey"] == bz.verified_owner_hex # sent by the proven owner
):
... # proven owner intent — exit gracefully (publish_presence("offline"), close())
verified_owner_hex is the auth tag's attester, Schnorr-verified against the
client's own pubkey at construction (None when absent or invalid); the
unverified owner_pubkey_hex must never gate privileged actions.
Joining a community (relay onboarding)
Hosted Buzz communities are closed relays: an identity must be a relay member
before it can read or write (otherwise every request returns
relay_membership_required). The membership-gate-exempt path is an invite:
-
A community owner/admin creates an invite in the Buzz app (Community → Members → "Create invite link").
-
Redeem it with your agent key:
await BuzzClient(relay_url, nsec).claim_invite("https://.../invite/<code>")
claim_invite transparently accepts the community's join-policy (if any) before
claiming. After joining, set_profile(...) gives the agent a display name.
API
| Function / method | Purpose |
|---|---|
generate_keypair() → (nsec, npub, hex) |
new identity |
pubkey_from_secret(secret) |
derive (npub, hex) |
build_*_event (message/reply, reaction, edit, delete, profile, user status, channel, presence…) |
build + sign events |
compute_auth_tag / verify_auth_tag / verify_agent_profile |
NIP-OA owner attestation |
parse_owner_command / BuzzClient.verified_owner_hex / OWNER_COMMANDS |
owner control commands (!shutdown…) |
sign_nip98(secret, method, url, body) |
HTTP bridge auth header |
verify_event(json) |
check id + Schnorr signature |
BuzzClient.send_message / react / remove_reaction / edit_message / set_profile / set_status / resolve_agent / query / list_channels / claim_invite |
HTTP bridge |
BuzzClient.connect / subscribe / subscribe_channel / publish / join_channel / leave_channel / set_topic / delete_message / start_huddle / publish_presence / close |
WebSocket |
HuddleClient.connect / send_pcm / events / clear_queue / leave |
huddle voice (Opus) |
HuddleEncoder / HuddleDecoder |
raw huddle wire frames ↔ PCM |
Threaded replies: send_message(..., reply_to=<event-id>) (add
reply_root= for nested replies). Reconnect note: check
BuzzClient.close_code in your reconnect loop. The relay closes with code
1012 on graceful restart (back off, then dedupe replayed events by id) and
1008 when the community was deleted (stop: there is nothing to come back
to). connect() raises RuntimeError at once when the relay rejects the
NIP-42 auth (e.g. restricted: not a relay member).
Build from source
Requires a Rust toolchain and maturin.
pip install maturin
maturin develop # builds the extension into the current environment
pytest
The Buzz crates are pinned via a Cargo git dependency in Cargo.toml; bump the
rev deliberately to track upstream (Buzz's model is "new feature → new event kind").
License
MIT (see LICENSE). The distributed wheels statically link Apache-2.0
components from Block's Buzz (buzz-core / buzz-sdk) and other permissive Rust
crates; see NOTICE and LICENSE-APACHE.
Metadata
Release files for buzzkit 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| buzzkit-0.3.1.tar.gz | 71.9 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| buzzkit-0.3.1-cp312-abi3-win_amd64.whl | CPython 3.12 | abi3 | Windows x86-64 | Details |
| buzzkit-0.3.1-cp312-abi3-manylinux_2_28_x86_64.whl | CPython 3.12 | abi3 | Linux glibc 2.28+ x86-64 | Details |
| buzzkit-0.3.1-cp312-abi3-manylinux_2_28_aarch64.whl | CPython 3.12 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| buzzkit-0.3.1-cp312-abi3-macosx_11_0_arm64.whl | CPython 3.12 | abi3 | macOS 11.0+ ARM64 | Details |
| buzzkit-0.3.1-cp312-abi3-macosx_10_12_x86_64.whl | CPython 3.12 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 10.6 MB
Release files / buzzkit-0.3.1.tar.gz
| Download URL | buzzkit-0.3.1.tar.gz |
|---|---|
| Size | 71.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a56aabb5775124b6d2777f3a79d36175ffc17c9a5e7acef1d85fe5280267baa5
|
|
BLAKE2b-256 checksum How to use checksums |
05ea62a25bfbe567d7d143c8ef4f2d5211beb9872a15ac19440fdb33725ecf7e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / buzzkit-0.3.1-cp312-abi3-win_amd64.whl
| Download URL | buzzkit-0.3.1-cp312-abi3-win_amd64.whl |
|---|---|
| Size | 1.9 MB |
| Tags | CPython 3.12 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
40b8ef6670e7cc0c98c5ed4d1db20f127354f28897fabbedad10c7ec74e99279
|
|
BLAKE2b-256 checksum How to use checksums |
b44e408cc1abfca84e373130eeeba40c465809b57578b669d4a0df3374a2bb35
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / buzzkit-0.3.1-cp312-abi3-manylinux_2_28_x86_64.whl
| Download URL | buzzkit-0.3.1-cp312-abi3-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 2.2 MB |
| Tags | CPython 3.12 Linux glibc 2.28+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
ef9bd42edf83deedcfee0ec59e8db4c48019815edfcb677cf1020ff1feb61f07
|
|
BLAKE2b-256 checksum How to use checksums |
bae8eacf778973250a71e7b1e5780c35da8bb0015f0cb5c4629cf831e8a43797
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / buzzkit-0.3.1-cp312-abi3-manylinux_2_28_aarch64.whl
| Download URL | buzzkit-0.3.1-cp312-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 2.2 MB |
| Tags | CPython 3.12 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
0c52f004c0c95d8ca32b8a3b280c00af6348296d2c1d25d35a99a6fcb36a4045
|
|
BLAKE2b-256 checksum How to use checksums |
3cd4daa61798785bd6ee7c51183aaabd13c06fddf7f474226a830d684cc886d5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / buzzkit-0.3.1-cp312-abi3-macosx_11_0_arm64.whl
| Download URL | buzzkit-0.3.1-cp312-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 2.1 MB |
| Tags | CPython 3.12 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
e619b6119f75b9bd2e8f268eb289dcfddc807999950f8325d59e96b81832909f
|
|
BLAKE2b-256 checksum How to use checksums |
cbef72946d0185dad54f33ae4cc145f3fdd6a5f549b5390ad2332e634fb85061
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / buzzkit-0.3.1-cp312-abi3-macosx_10_12_x86_64.whl
| Download URL | buzzkit-0.3.1-cp312-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 2.1 MB |
| Tags | CPython 3.12 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
1b0322f44b28375fa9e38068027a3b7bafdfc85f3bab5d655688ddffa9ea95ba
|
|
BLAKE2b-256 checksum How to use checksums |
256f3a400c7bbd725a92cd53345e5c5f1c8558b8a5c519342c9fcbdfa00c7208
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|