feat: Add `buzz invites mint` and `buzz invites claim` commands
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
**Duplicate check:** none found. Searched [open issues and PRs](https://github.com/block/buzz/issues?q=is%3Aopen) for "invite", "invites claim", "invites mint", no matches.
## Motivation
The relay has a complete invite API (`POST /api/invites` to mint codes, `POST /api/invites/claim` to redeem them), but `buzz-cli` exposes neither operation.
1. Agents and CLI users can't join a locked-down relay via invite link. `buzz channels join` sends a NIP-29 join-request event (kind 9021), which the relay rejects with `403 relay_membership_required` if the caller isn't already a relay member. The only path for a new identity to join is `POST /api/invites/claim`, which is deliberately exempt from the membership gate (`api/invites.rs:7`). The CLI has no command that calls this endpoint.
2. Admins can't mint invite codes without the desktop app. `POST /api/invites` is owner/admin-only and returns a code + landing-page URL. The desktop client uses this to generate shareable links, but a headless relay admin has no CLI equivalent. The desktop client also only generates invite URLs; there's no way to invite a specific pubkey directly.
This particularly affects agents running outside the Buzz desktop app (e.g. a Hermes Agent bridged into Buzz via a community gateway plugin). The repo's own AGENTS.md states:
> **Agent-facing operations go in buzz-cli** — add a subcommand there first, then wire the REST/WebSocket call in `client.rs`.
Without a CLI claim command, the only onboarding path is for a relay admin to manually run `buzz-admin add-member` with the agent's pubkey out-of-band. This requires key exchange outside the invite system and doesn't scale for multi-agent deployments or self-service onboarding.
## Proposed solution
Add an `invites` subcommand to `buzz-cli`:
```bash
# Mint an invite code (owner/admin only)
buzz invites mint [--ttl-secs 259200] # default: 72h, max: 30d
# Claim an invite code (any identity, membership-gate exempt)
buzz invites claim --code
# (optional) direct invite by pubkey — mint + add-member in one step
buzz invites add-member --pubkey --role member
```
Implementation is straightforward given existing patterns. No relay changes, no new Nostr event kinds:
1. New `InvitesCmd` enum in `commands/` (or extend `ChannelsCmd`)
2. Two `client.rs` methods using the existing `sign_nip98` + authenticated POST pattern (`client.rs:84`, `client.rs:783+`):
- `mint_invite(ttl_secs)` → `POST /api/invites` → `{ code, expires_at, url }`
- `claim_invite(code)` → `POST /api/invites/claim` with `{ code }` body → `{ status, community_id, host, role }`
3. `invites` subcommand wired into the CLI clap structure
4. Optional: `--policy-receipt` flag for relays with join-policy enforcement
## Alternatives considered
Manual `buzz-admin add-member` for each agent. Works today but requires out-of-band pubkey exchange, admin availability, and doesn't use the invite system at all. Fine for one agent, doesn't scale for self-service onboarding or multi-agent deployments.
Extend `channels join` to accept invite codes. Considered but rejected. `channels join` sends a kind 9021 Nostr event (a join *request*), while `POST /api/invites/claim` is a REST call that bypasses the event pipeline entirely. Overloading `join` with two different code paths would be confusing.
Add invite claim to `buzz-admin` instead of `buzz-cli`. `buzz-admin` is operator-only (relay administration). Invite *claiming* is a user/agent operation. It belongs in the agent-first CLI alongside `channels join` and `messages send`.
## Additional context
What already exists (verified against source at `block/buzz@main`):
| Layer | Status | Location |
|---|---|---|
| Relay: mint endpoint | ✅ | `POST /api/invites` — `api/invites.rs:230` |
| Relay: claim endpoint | ✅ | `POST /api/invites/claim` — `api/invites.rs:291` |
| Token format (HMAC-signed, stateless) | ✅ | `invite_token.rs` |
| NIP-98 auth signing in CLI | ✅ | `client.rs:84` (`sign_nip98`) |
| Authenticated POST in CLI | ✅ | `client.rs:783+` |
| `buzz-admin add-member` (manual relay membership) | ✅ | `buzz-admin` |
| `buzz channels join` (kind 9021, requires existing membership) | ✅ | `commands/channels.rs:896` |
| **`buzz invites mint`** | ❌ | — |
| **`buzz invites claim`** | ❌ | — |
Concrete use case: an AI agent (Hermes, ACP harness, or any external system) receives an invite link from a relay admin. The agent should be able to run `buzz invites claim --code eyJjIjoiMWY2...` and join the relay without manual admin intervention. Today this requires the admin to run `buzz-admin add-member` out-of-band.
Related: the desktop client also lacks the ability to invite a specific pubkey directly (only generates invite URLs). The optional `invites add-member` command would fill that gap for both CLI and desktop users.
Contributor guide
Research direction
Start with commands/channels.rs:896 for the CLI command and client.rs:84 and 783+ for NIP-98 signing and authenticated POST requests. Read api/invites.rs:230 and :291 to confirm the mint and claim request and response shapes. Done means buzz invites mint and buzz invites claim are wired into the CLI and call the existing endpoints with their documented options.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, cli
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100