Skip to content

fix(cli): require --channel on repos create for buzz-channel tag - #3594

Open
Chessing234 wants to merge 2 commits into
block:mainfrom
Chessing234:fix/repos-create-buzz-channel
Open

fix(cli): require --channel on repos create for buzz-channel tag#3594
Chessing234 wants to merge 2 commits into
block:mainfrom
Chessing234:fix/repos-create-buzz-channel

Conversation

@Chessing234

Copy link
Copy Markdown
Contributor

Summary

Test plan

  • cargo test -p buzz-sdk --lib builders::tests::repo_announcement
  • buzz repos create --id demo --channel <uuid> --clone https://<relay>/git/<pubkey>/demo then git ls-remote succeeds
  • Omitting --channel fails clap validation

Fixes #3539

Made with Cursor

Chessing234 and others added 2 commits July 29, 2026 21:30
Without a buzz-channel binding the relay git read gate returns an opaque
404 for every clone/fetch/push. Make the channel UUID required and emit
the tag from build_repo_announcement.

Signed-off-by: Taksh <takshkothari09@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Taksh <takshkothari09@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@Chessing234
Chessing234 requested a review from a team as a code owner July 29, 2026 18:30

@tlongwell-block tlongwell-block left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed as part of the #3527 investigation (root cause is the same bug — see the linked discussion; #3539 and #3527 should be cross-linked and one closed as a duplicate).

Verified locally at head 6bbe384:

  • build_repo_announcement has exactly one production caller (cmd_create_repo), so the signature change is contained — checked with rg across crates/.
  • cargo test -p buzz-sdk --lib: 241 passed. cargo test -p buzz-cli: green.
  • The SDK parameter staying Option<&str> while the CLI flag is required matches the design consensus from the team thread.

Two non-blocking notes:

  1. validate_uuid is parse-only — a well-formed UUID for a nonexistent channel (or one the announcer is not a member of) still produces an unreadable repo. Acceptable for this PR; a follow-up adds owner remediation messaging at the read gate and, later, ingest-time binding validation (tracked in the #3527 workstream).
  2. The two unchecked live-test boxes in the test plan will be covered by our live regression matrix against a local Docker stack before this merges, alongside the follow-up PR.

The follow-up PR (buzz repos bind for existing unbound repos + read-gate remediation) composes with this change and does not touch these files. LGTM.

tlongwell-block added a commit that referenced this pull request Jul 30, 2026
…pos (#3626)

Closes #3527.

Repos announced via vanilla NIP-34 (kind:30617 without a `buzz-channel`
tag) 404 forever: the SEC-005 read gate requires a channel-membership
ACL, and nothing tells the author why or how to fix it. Per the ruling
in the originating thread, this ships **bind/rebind tooling plus a
narrow author-only remediation carve-out** — the shelved owner-circle
approach is intentionally absent.

## Relay
- **`api/git/binding.rs` (new):** shared tri-state binding resolver —
`Bound(uuid)` / `NotBound` / `Broken`. First-tag, fail-closed: a
malformed `buzz-channel` tag is `Broken`, never conflated with "no tag".
Both gates use it.
- **Read gate (`transport.rs`):** a **never-bound** repo read by **its
own announcement author** still returns 404 (status byte-identical to
the generic denial) but the body carries remediation: `run: buzz repos
bind --id <repo> --channel <channel-uuid> — …`. This leaks nothing — the
author announced the repo, and only the author can rebind (30617 is
keyed by `(author, d)`). `Broken` bindings stay generic-denial for
everyone, including the author (revocation shape).
Bound-to-nonexistent-channel stays generic (phase 1; ingest validation
is phase 2).
- **Push gate (`policy.rs`):** unbound denial now returns
`GIT_NO_CHANNEL_BINDING_BODY`. A deploy-skew test pins that the body
carries both the new token (`no_channel_binding`) and the legacy phrase
(`"no channel binding"`) so already-shipped desktops keep matching.
**(Review r1, blocker 2)** `Broken` no longer collapses into "unbound":
it denies 403 `invalid channel binding` for *everyone — including the
announcement owner —* **before** the owner short-circuit, matching the
read gate's fail-closed posture. The remediation token stays
NotBound-only.
- **`ingest.rs`:** side-effect failure `warn!` → `error!` — prod runs
`RUST_LOG=error`, so these failures were invisible during triage.

## Contract
- **`buzz-core/git_perms.rs`:** `GIT_NO_CHANNEL_BINDING_TOKEN` /
`GIT_NO_CHANNEL_BINDING_BODY` consts as the declared cross-component
contract; relay tests and desktop matcher both build on them.

## CLI
- **`buzz repos bind --id <repo> --channel <uuid>`** — rebinds an
existing announcement, preserving other tags.
- **(Review r1, blocker 1)** **`--channel` on `buzz repos create`** —
optional; injects exactly one shape-validated `buzz-channel` tag at
creation via a pure `build_create_announcement` builder, so the primary
create command stops producing repos the relay 404s. UUID
existence/membership stays the relay's authority at git-access time
(same TOCTOU posture as `repos bind`). Overlaps with #3594 (open, head
6bbe384) — happy to reconcile whichever lands first; this branch also
carries the bind path and tag preservation.

## Desktop
- **Rust:** new `commands/project_git_merge_error.rs` (extracted from
`project_git_workflow.rs` to respect the 1000-line ratchet); maps the
token to a structured `no_channel_binding` error carrying the bind
command.
- **TS:** new `features/projects/lib/projectBranchErrors.ts` + tests —
dual matcher (new token AND legacy spaced phrase);
`ProjectBranchDialogs.tsx` uses it.

## Tests / verification (at head f914c70, base 581baa6)
- Workspace `cargo test` green; `clippy -D warnings` clean; desktop Rust
1859 pass; TS 3780 pass; tsc/biome/file-size checks pass. Pre-push hooks
re-ran all suites at the pushed head.
- Postgres-gated `sec005_read_gate_tests`: all 6 pass, including
`read_gate_gives_author_of_unbound_repo_remediation_body` — asserts 404
status, `text/plain` content-type, and exact body bytes, distinguishing
remediation from generic denial (a blind `is_err()` can't).
- **New (review r1):** `buzz-cli` emitted-event tests —
`create_with_channel_emits_exactly_one_binding_tag`,
`create_without_channel_emits_no_binding_tag`,
`create_rejects_malformed_channel_uuid` (266/266 pass). Postgres-gated
`push_gate_denies_owner_through_broken_binding` — owner +
malformed-first/valid-second binding → 403 generic body without the
remediation token; never-bound control stays 200, pinning the denial to
`Broken` specifically.
- e2e git tests now bind announcements to a real channel via a
`create_test_channel` helper.

---------

Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Co-authored-by: npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: buzz repos create omits the buzz-channel tag that the git read gate requires — every CLI-created repo 404s forever

2 participants