block / block/buzz

Relay client policy (signing, auth, scoping, retries, delivery outcomes) is locked inside buzz-cli and reimplemented per client

Open
#5,471 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
32.7k
Forks
4.3k
Avg merge
1d 13h
Merged PRs (30d)
253

Description

## Summary

The correctness-sensitive parts of talking to a Buzz relay — signing, NIP-42/98 authentication, community scoping, pagination, retries, and interpreting ambiguous delivery outcomes — currently live in `crates/buzz-cli/src/client.rs`. Anything that is not the CLI has to reimplement them.

`buzz-ws-client` sits below this as shared transport (connect, auth, publish), but the policy layer above it is CLI-private. So the desktop app, the mobile app, and any third-party client each end up with their own version of the same rules.

## Why this matters

These are not conveniences; they are the parts where being subtly wrong is invisible until it is expensive:

- **Delivery outcomes are ambiguous by nature.** A publish that times out may or may not have been stored. A client that guesses wrong either drops a message or double-sends.
- **Community scoping is a correctness boundary**, not a formatting detail — `h`-tag scoping and the host-derived community boundary have to be applied consistently or a client can read across a boundary it should not.
- **Retry policy interacts with signing.** Re-signing on retry versus reusing the signed event is exactly the kind of decision that gets made differently in each reimplementation.

Every independent reimplementation is a fresh opportunity to get one of these wrong, and a bug fixed in one client does not reach the others.

## Concretely

I hit this trying to start a separate client: the logic I needed was reachable only by depending on the CLI crate, which drags in argument parsing, output formatting, and environment assumptions that have nothing to do with talking to a relay.

## Proposed shape

Extract a `crates/buzz-client` library that owns the policy layer, with `buzz-ws-client` remaining the transport beneath it:

- explicit community endpoint + configuration rather than ambient environment
- an async signer abstraction, so key custody is the caller's decision (CLI env var, desktop keychain, mobile secure storage) instead of being baked in
- typed errors and results, and **semantic** delivery outcomes rather than a bare `Result` that loses the stored-but-unacknowledged case
- no CLI, Tauri, or environment concerns in the public API

The migration wants to be incremental rather than a big-bang move: establish the intended final boundary immediately, but move only a couple of operations across it first, keeping existing CLI behavior byte-stable and proving the same public API serves both the CLI and an independent consumer.

## What I'd like acknowledged before more of it gets built

This is an architectural change and CONTRIBUTING.md asks for the approach to be agreed first, so the questions worth answering are:

1. Is `crates/buzz-client` above `buzz-ws-client` the boundary you want, or should this land inside `buzz-ws-client` instead?
2. Is incremental migration acceptable, with two client paths coexisting while operations move across one at a time?
3. Is a public, semver-exposed client library something the project wants to own and support at all? A reasonable answer is "no, keep it internal" — better to hear that now than after the migration.

## Status

Filed after the fact: #4717 already implements the first slice (channels list + messages send migrated, everything else left on the existing path) and is open as a draft. Filing here so the boundary question can be settled independently of reviewing that diff — if the answer to (1) or (3) is different from what I assumed, the diff is the wrong thing to be discussing.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.