Skip to content

feat(git): add channel-scoped private repositories#2539

Open
LordMelkor wants to merge 1 commit into
block:mainfrom
LordMelkor:feat/private-repo-channel-access
Open

feat(git): add channel-scoped private repositories#2539
LordMelkor wants to merge 1 commit into
block:mainfrom
LordMelkor:feat/private-repo-channel-access

Conversation

@LordMelkor

Copy link
Copy Markdown

Closes #2469

What

Add explicit private visibility for Buzz-hosted Git repositories. A private
repository is discoverable and usable only by its repository owner, the verified
owner of a managed-agent repository, or a current member of its bound Buzz
channel.

This closes both read paths:

  • repository discovery and browsing through kind 30617 announcements, kind
    30618 state events, WebSocket/HTTP query and count paths, search,
    pagination, and live fan-out;
  • Smart HTTP Git access through info/refs, git-upload-pack, and
    git-receive-pack.

Denied Git requests return the same generic 404 repository not found response
as an absent repository. Repositories without an explicit private marker retain
their current behavior.

How

  • Add explicit repository metadata:
    • ["buzz-visibility", "private"]
    • ["buzz-channel", "<channel-uuid>"]
  • Validate kind 30617 announcements before storage. Private metadata must be
    unambiguous and well formed, and the announcing owner must currently belong to
    the selected channel.
  • Centralize repository access policy in
    crates/buzz-relay/src/api/git/access.rs; relay query, count, search,
    fan-out, HTTP bridge, and Smart HTTP handlers all delegate to that policy.
  • Gate repository announcements and state events after database retrieval so
    hidden rows cannot consume visible query limits or pagination slots.
  • Compute repository discovery counts after access filtering. Counts with more
    than 20,000 candidates are rejected with a request for narrower constraints
    rather than returning a truncated value.
  • Gate all Smart HTTP endpoints before repository hydration or Git subprocess
    work and fail closed on lookup or authorization errors.
  • Add --visibility and --channel to buzz repos create, plus buzz repos edit for visibility changes.
  • Add Desktop creation and owner-only visibility controls, and display
    visibility in Desktop and web repository views.
  • Preserve a legacy buzz-channel tag as a write-policy binding when read
    visibility is public.
  • Document the new tags, access rules, compatibility behavior, and CLI/Desktop
    surfaces.

Key decisions

Relay rejection is the visibility-edit UX

The relay remains authoritative for whether a private repository owner currently
belongs to the selected channel. Clients do not duplicate that policy with a
racy preflight check. Desktop surfaces the relay's OK rejection message in its
toast, and the CLI includes the same relay message in its error. For example, an
invalid update reports that the private repository owner must be a current
member of buzz-channel.

Authorization uses the current kind 30617 announcement on every request

Each discovery or Smart HTTP request resolves the current repository
announcement and then checks current channel membership. This is deliberate live
authorization: removing a member revokes access on the next request, without
stale repository ACL state or a second source of truth. The extra lookup is
preferred over cached or denormalized authorization because correctness and
immediate revocation are the primary requirements.

Privacy is explicit and backward compatible

Only an exact ["buzz-visibility", "private"] opt-in makes a repository
private. A pre-existing buzz-channel tag by itself continues to control push
policy only, so existing repositories do not silently change read visibility.
Changing a private repository back to public preserves that push binding.

Denials do not reveal repository existence

Authorization failures and missing repositories use the same 404 repository not found response on Smart HTTP endpoints. Malformed private discovery metadata
and lookup failures fail closed.

Manual test

  1. Create a channel with a repository owner and a second member. Keep a third
    authenticated user outside the channel.

  2. In Desktop’s create dialog, choose the Private radio card. Confirm the
    channel picker appears only for Private, select the channel, and create the
    project. With an identity that belongs to no eligible non-DM channels,
    confirm the dialog says Join a channel to create a private project and
    keeps Create project disabled.

  3. Confirm a Lock icon marks the private project in grid, list, and overview
    rail rows. Open the project as the owner, open the Private · #channel
    badge menu, and confirm Public and the eligible channel choices are separate
    menu decisions rather than one flat list.

  4. Open the project as the second channel member. Confirm the read-only badge
    says Private · #channel and its tooltip says only channel members and the
    owner can access the project.

  5. Confirm the owner and channel member can find the project, open its
    repository page, and clone or run git ls-remote against its exact clone
    URL.

  6. Confirm the non-member cannot find the project through listing or search.
    Give the non-member the exact clone URL and confirm git ls-remote reports
    404 repository not found.

  7. Remove the second member from the channel. Without restarting the relay or
    client, repeat discovery and git ls-remote; confirm access is denied on the
    next request.

  8. As the owner, choose Public from the project badge menu. Confirm an
    AlertDialog asks Make this project public? and warns that anyone in the
    workspace will be able to find and clone it. Cancel once and confirm nothing
    changes; then confirm the transition and verify the former non-member can
    discover and read it while the existing channel-based push policy remains
    intact. Confirm Public→Private and private-channel changes do not ask for
    confirmation.

  9. Exercise the CLI equivalents:

    buzz repos create \
      --id private-demo \
      --clone <clone-url> \
      --visibility private \
      --channel <channel-uuid>
    
    buzz repos edit \
      --id private-demo \
      --visibility public
  10. Attempt to create or update a private repository with a channel the owner has
    not joined. Confirm Desktop and CLI display the relay rejection message.

  11. Verify a legacy repository that has buzz-channel but no buzz-visibility
    tag remains readable under the existing rules.

Deferred work

None. This PR includes relay enforcement, discovery filtering, Git endpoint
protection, CLI and Desktop controls, web visibility display, compatibility
behavior, documentation, and automated coverage.

Validation

  • just ci passes with the repository-pinned Hermit toolchain at
    0482aba696decd3cc518ff04d68147eac0f38d42.
  • just test-integration passes at
    0482aba696decd3cc518ff04d68147eac0f38d42.
  • New behavior has unit and Postgres-backed integration coverage, including
    all Smart HTTP endpoints and the over-20,000 discovery-count rejection
    path.
  • New repository metadata and user-facing behavior are documented.
  • No new unwrap() in production code paths.
  • No new unsafe blocks.

@LordMelkor
LordMelkor requested a review from a team as a code owner July 23, 2026 16:06
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.

feat(git): private repositories with channel-scoped access

1 participant