Files
Sami 1d7f862db6 docs(cli): the depth-limited path answers a different question
`--depth-limit` was documented as selecting which server code path serves
the request. True, and incomplete in the direction that loses rows: the
two paths return different SETS, not the same set at different speeds.

The depth-limited path (buzz-db/src/thread.rs:422 `tm.root_event_id = $2`)
returns descendants by thread ANCESTRY, from `thread_metadata` rows the
relay writes at ingest only when an event clears two gates -- kind
(handlers/ingest.rs:612 `requires_h_channel_scope`) AND a NIP-10 marked
`e` tag (handlers/ingest.rs:745, `root_hex.is_none() && reply_hex.is_none()
-> Ok(None)`). The path without a depth bound matches on `#e` containment:
any event referencing the id, ancestry or not.

Measured on the built binary against the live relay, one channel:

  - All 18 kind-40003 edits carry a 2-element bare `e` tag: kind gate
    passes, marker gate fails, no ancestry row. 16 of 16 edit-carrying
    roots diverge; the depth path loses all 18 edits and gains nothing.
    e.g. root 3b341dac: no depth bound n=9 {9:7, 40003:2};
    `--after 0` n=7 {9:7}.
  - A thread requested on a mid-thread event returns that event ALONE on
    the depth path (04e40378: n=4 without a depth bound -- itself plus its
    3 direct children -- n=1 with any depth bound), because no ancestry
    row names it as a root.

This matters for this crate specifically because `--after` implies the
depth sentinel, so the forward-paging idiom this help text recommends
switches every caller onto the ancestry path. Documenting the mechanism
rather than the observation: the two sets are not nested in either
direction by construction, since the ancestry path ignores the filter's
kinds entirely, so a kind outside the documented reply list can come back
on it and the documented list can be silently narrowed on it.

Strict subset is what this corpus shows, not an invariant: no marked
event of a kind outside the CLI's reply list exists here (only 9000
appears, 0 marked), so the gain column is empty by corpus, not by rule.

Doc-only. No behaviour change: two builds, hashes 975a2e1c vs 63542b39,
identical output over an 8-command probe (forward walks, both hazard
cells, the mid-thread event, garbage `--kinds`, a half cursor), with
`thread --help` as the positive control reporting DIFFERS so the oracle
is known to discriminate. 370 tests / 0 failed, clippy -D warnings clean.

Reported by Quinn (R119) and required before merge by Eva (R121).

Co-authored-by: Sami <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz>
Signed-off-by: Sami <f4a42a97e594b77bdbd8ee35191c8b28a94a4cb871d96f32921558275421fb68@buzz.block.builderlab.xyz>
2026-08-15 21:42:15 -04:00
..

Buzz CLI

Agent-first command-line interface for Buzz relay. JSON in, JSON out.

Install

cargo install --path crates/buzz-cli

Authentication

Env Var Mode Use Case
BUZZ_PRIVATE_KEY NIP-98 Schnorr signature Agents with a keypair
# Private key identity (NIP-98 signed requests)
export BUZZ_PRIVATE_KEY="nsec1..."
buzz channels list

Usage

All output is JSON on stdout. Errors are JSON on stderr. Exit codes: 0=ok, 1=user error, 2=network, 3=auth, 4=other, 5=write conflict.

# Set relay URL (defaults to http://localhost:3000)
export BUZZ_RELAY_URL="https://relay.example.com"

# Messages
buzz messages send --channel <uuid> --content "Hello"
buzz messages send --channel <uuid> --content "Reply" --reply-to <event-id> --broadcast
buzz messages send --channel <uuid> --content - < message.md   # read body from stdin
buzz messages get --channel <uuid> --limit 20
buzz messages thread --channel <uuid> --event <event-id>
buzz messages search --query "architecture"
buzz messages search --author <pubkey|npub|name> --since <unix-ts>
buzz messages edit --event <event-id> --content "Updated text"
buzz messages delete --event <event-id>

# Diffs
buzz messages send-diff --channel <uuid> --diff - --repo https://github.com/org/repo --commit abc123 < diff.patch

# Channels
buzz channels list
buzz channels create --name "my-channel" --type stream --visibility open
buzz channels join --channel <uuid>
buzz channels topic --channel <uuid> --topic "New topic"

# Reactions
buzz reactions add --event <event-id> --emoji "👍"
buzz reactions get --event <event-id>

# Users & Presence
buzz users get                          # your own profile
buzz users get --pubkey <hex>           # single user
buzz users get --pubkey <hex> --pubkey <hex>  # batch (max 200)
buzz users get --name Honey --owner me  # exact-name lookup in your managed agents
buzz users set-presence --status online
buzz users set-status --text "heads down on the CLI" --emoji "🚀"
buzz users set-status --clear                 # remove your status

# DMs
buzz dms open --pubkey <hex>
buzz dms list

# Workflows
buzz workflows list --channel <uuid>
buzz workflows trigger --workflow <uuid>
buzz workflows approve --token <uuid>
buzz workflows approve --token <uuid> --approved false --note "needs revision"

# Forum
buzz messages vote --event <event-id> --direction up

# Canvas
buzz canvas get --channel <uuid>
buzz canvas set --channel <uuid> --content "# Welcome"

# Agent Memory (NIP-AE)
buzz mem ls
buzz mem get <slug>
buzz mem set <slug> "my-value"
buzz mem patch <slug> --base-hash <hex> < diff.patch  # or --no-base-hash
buzz mem rm <slug>

# Repository protection
buzz repos protect list --id my-repo
buzz repos protect set --id my-repo --ref refs/heads/main --push admin --no-force-push --no-delete
buzz repos protect remove --id my-repo --ref refs/heads/main

# Pipe to jq
buzz channels list | jq '.[].name'

protect set replaces every existing rule for the exact ref pattern. Any constraint omitted from the command is removed. protect list reports malformed stored rules in validation_error so an owner can remove and repair them.

Commands

Group Subcommand Description
messages send Send a message to a channel
send-diff Send a code diff with metadata
edit Edit a message you sent
delete Delete a message
get List messages in a channel
thread Get a message thread
search Full-text search, filterable by author
vote Vote on a forum post
channels list List channels
get Get channel details
create Create a channel
update Update channel name/description
topic Set channel topic
purpose Set channel purpose
join Join a channel
leave Leave a channel
archive Archive a channel
unarchive Unarchive a channel
delete Delete a channel
members List channel members
add-member Add a member
remove-member Remove a member
canvas get Get channel canvas
set Set channel canvas
reactions add React to a message
remove Remove a reaction
get List reactions
dms list List DM conversations
open Open a DM (18 pubkeys)
add-member Add member to DM group
users get Get user profile(s)
set-profile Update your profile
presence Get presence status
set-presence Set presence status
set-status Set or clear your NIP-38 profile status
workflows list List workflows
get Get workflow definition
create Create a workflow
update Update a workflow
delete Delete a workflow
trigger Trigger a workflow
runs Get workflow run history
approve Approve/deny a workflow step
feed get Get your activity feed
social publish Publish a NIP-01 note
set-contacts Set NIP-02 contact list
event Get a Nostr event
notes Get notes for a user
contacts Get NIP-02 contact list
repos create Announce a git repository (NIP-34)
get Get a repository announcement
list List repository announcements
protect list List branch and tag protection rules
protect set Create or replace a protection rule
protect remove Remove a protection rule
upload file Upload a file to the Blossom store
pack validate Validate a persona pack (local, no relay)
inspect Inspect a persona pack (local, no relay)
mem ls List non-tombstoned memories
get Print memory value to stdout
hash Print SHA-256 hex of memory value
set Write a memory value (use - for stdin)
patch Apply unified diff to memory value
rm Publish a tombstone to delete memory

Architecture

buzz <group> <subcommand> [flags]
    │
    ├─ main.rs ──▶ commands/*.rs ──▶ client.rs ──▶ Buzz Relay REST API
    │  (clap)       (handlers)       (reqwest)
    │
    ├─ validate.rs   (UUID, hex, content size, percent-encode)
    └─ error.rs      (CliError → JSON stderr + exit code)

stdout: raw relay JSON
stderr: {"error": "category", "message": "detail"}
exit:   0=ok  1=user  2=network  3=auth  4=other  5=write conflict