Skip to main content

Python bindings + async client for Block's Buzz (Nostr) protocol, backed by Rust buzz-core/buzz-sdk

Project description

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"]

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:

  1. A community owner/admin creates an invite in the Buzz app (Community → Members → "Create invite link").

  2. 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
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: the relay closes with code 1012 on graceful restart, so check BuzzClient.close_code in your reconnect loop and dedupe replayed events by id.

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

buzzkit-0.2.0.tar.gz (63.1 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

buzzkit-0.2.0-cp312-abi3-win_amd64.whl (1.9 MB view details)

Uploaded CPython 3.12+Windows x86-64

buzzkit-0.2.0-cp312-abi3-manylinux_2_28_x86_64.whl (2.2 MB view details)

Uploaded CPython 3.12+manylinux: glibc 2.28+ x86-64

buzzkit-0.2.0-cp312-abi3-manylinux_2_28_aarch64.whl (2.2 MB view details)

Uploaded CPython 3.12+manylinux: glibc 2.28+ ARM64

buzzkit-0.2.0-cp312-abi3-macosx_11_0_arm64.whl (2.1 MB view details)

Uploaded CPython 3.12+macOS 11.0+ ARM64

buzzkit-0.2.0-cp312-abi3-macosx_10_12_x86_64.whl (2.1 MB view details)

Uploaded CPython 3.12+macOS 10.12+ x86-64

File details

Details for the file buzzkit-0.2.0.tar.gz.

File metadata

  • Download URL: buzzkit-0.2.0.tar.gz
  • Upload date:
  • Size: 63.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for buzzkit-0.2.0.tar.gz
Algorithm Hash digest
SHA256 f73572e9894e133e7b89bed58597bb7443aa660ea6eaa2f9992a58001ff06f76
MD5 df1111175618f1d0832147cbf1ea779d
BLAKE2b-256 c8b3f0a05808d7fc31425761ed38a2a8a181aabc9a7972c85b37ee3885c9bd72

See more details on using hashes here.

File details

Details for the file buzzkit-0.2.0-cp312-abi3-win_amd64.whl.

File metadata

  • Download URL: buzzkit-0.2.0-cp312-abi3-win_amd64.whl
  • Upload date:
  • Size: 1.9 MB
  • Tags: CPython 3.12+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for buzzkit-0.2.0-cp312-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 1ebfd9db6854143b99c8713576274773c4958919dec771053b22f189c3234ea5
MD5 7c67a94403cb2a16adc7ad3d7b240139
BLAKE2b-256 3072b9a779cc92091699d085d9fa086ec303a45e81142db13fea5df9e03d6887

See more details on using hashes here.

File details

Details for the file buzzkit-0.2.0-cp312-abi3-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for buzzkit-0.2.0-cp312-abi3-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 b859554234d0529450a4132114481502acda08db34af91fac6876e3f2640661c
MD5 f307b5e178a7ea92fed743dce5e59f71
BLAKE2b-256 1cb379b9ab7c58e700fa72d01a4767d3a57bad49e2ac6ba45cdb1bd778f5a178

See more details on using hashes here.

File details

Details for the file buzzkit-0.2.0-cp312-abi3-manylinux_2_28_aarch64.whl.

File metadata

File hashes

Hashes for buzzkit-0.2.0-cp312-abi3-manylinux_2_28_aarch64.whl
Algorithm Hash digest
SHA256 47c4e2360acb6710c00cbeb562150b80619fa58a6894259dadbc3c857373e32f
MD5 4f35ce90a84b0a2959f057341d0f887b
BLAKE2b-256 97150dfde42ca50eafc273cec3043f4f9e75014ab8b861cb4fa91f06489819b1

See more details on using hashes here.

File details

Details for the file buzzkit-0.2.0-cp312-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for buzzkit-0.2.0-cp312-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 dd5501714b08f30be8c13e077e71b10a0697ab64b4a9a28555a7ec95083e6564
MD5 3750b3ee0b8cdb8ec2923641f0ae95e7
BLAKE2b-256 ad538e1d6f802cd0fc890e5306101d7dc5e0ef56cb95d137bf1a4c39ed79c752

See more details on using hashes here.

File details

Details for the file buzzkit-0.2.0-cp312-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for buzzkit-0.2.0-cp312-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 1e192a9e546f6d3a2be519c952fd8e9968164261cc21198da97a9983888f1b83
MD5 d9909439dbc8a6aadad9e0cbae4c861c
BLAKE2b-256 c32dfb4f73cd9c037432b9a3b006d610001724d76c13c6f09f30559d5ed3f517

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page