fix(cli): require --channel on repos create for buzz-channel tag - #3594
fix(cli): require --channel on repos create for buzz-channel tag#3594Chessing234 wants to merge 4 commits into
Conversation
tlongwell-block
left a comment
There was a problem hiding this comment.
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_announcementhas exactly one production caller (cmd_create_repo), so the signature change is contained — checked withrgacrosscrates/.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:
validate_uuidis 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).- 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.
…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>
Optional binding left new repos on an opaque git 404 until a later bind. Require the UUID up front so announcements always carry buzz-channel (block#3539). Signed-off-by: Taksh <takshkothari09@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
build_create_announcement now takes a required UUID and attaches exactly one binding tag; drop the optional no-binding path. Signed-off-by: Taksh <takshkothari09@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the managed-agent skill aligned with the required clap flag. Signed-off-by: Taksh <takshkothari09@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Surface the git ACL requirement in clap after_help so operators see it before hitting a relay 404. Signed-off-by: Taksh <takshkothari09@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
|
rebased onto current main (was conflicting). --channel is now required on top of the optional path that landed meanwhile; nest skill + clap help updated. create_with_channel / malformed uuid tests still green. |
6bbe384 to
8d3f063
Compare
|
rebase onto main is done and checks should be green again — still looking for a merge when convenient (approved, mergeable). |
|
friendly bump — still approved + mergeable when you have a free merge slot. |
|
still approved + mergeable + dco green — happy to rebase again if main moves. |
|
still approved + mergeable whenever you have a free merge. |
|
friendly bump — still approved + mergeable. |
Summary
--channel <UUID>onbuzz repos createand emit abuzz-channeltag on the kind:30617 announcementbuzz repos createomits thebuzz-channeltag that the git read gate requires — every CLI-created repo 404s forever #3539)build_repo_announcementand update the nest skill exampleTest plan
cargo test -p buzz-sdk --lib builders::tests::repo_announcementbuzz repos create --id demo --channel <uuid> --clone https://<relay>/git/<pubkey>/demothengit ls-remotesucceeds--channelfails clap validationFixes #3539
Made with Cursor