Skip to content

Apply optional relay join policy across join flows - #1894

Merged
tlongwell-block merged 20 commits into
mainfrom
wren/invite-terms-gate
Jul 16, 2026
Merged

Apply optional relay join policy across join flows#1894
tlongwell-block merged 20 commits into
mainfrom
wren/invite-terms-gate

Conversation

@tlongwell-block

@tlongwell-block tlongwell-block commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • replace URL-specific invite terms with a generic, opt-in relay join policy configured by full Terms Markdown, full Privacy Markdown, and an independent 18+ requirement
  • expose GET /api/join-policy for every join surface and derive a SHA-256 policy version from the exact configured content
  • gate web invite acceptance, explicit first-run desktop relay and invite joins, membership-denied redemption, and bottom-left Add Community with shared policy UI
  • keep generic Welcome and existing-key import consent-free until the user identifies a community
  • keep unconfigured and older relays on the existing direct join behavior
  • enforce configured invite claims server-side with a short-lived relay-authenticated receipt bound to the invite code and current policy version

Configuration

  • BUZZ_TERMS_OF_SERVICE_MARKDOWN
  • BUZZ_PRIVACY_POLICY_MARKDOWN
  • BUZZ_AGE_ATTESTATION_REQUIRED

Each value is optional; no configured values means no policy. Invalid age booleans fail configuration parsing.

Evidence boundary

Configured invite claims cannot add a relay member without a valid current policy receipt. Direct URL/name onboarding and Add Community without an invite are client-side connection flows rather than relay membership mutations, so this PR does not claim a durable per-pubkey acceptance record for those paths. Whether Block needs separate persisted evidence remains a product/legal decision.

Validation

  • pre-push hook green with Hermit toolchain: Rust package tests, desktop JS tests, desktop Tauri tests, mobile tests
  • desktop typecheck and all 2,888 JS tests pass (includes 3 focused join-policy API tests)
  • desktop full check passes with two unrelated existing unused-variable warnings
  • desktop deep-link Rust tests: 27 pass
  • web check and production build pass
  • relay policy receipt binding and invalid-age config tests pass
  • focused onboarding, existing-key, Add Community, legal-link, and browser invite coverage passes
  • live local relays verified policy ON (/api/join-policy returned Markdown + age + hash; button disabled in Chromium) and OFF ({}; normal active link)

Known unrelated issue

  • the prior full relay run had one existing mesh demo timeout (504 vs 200); targeted changed tests pass

UI snapshots

Welcome before a community is known

No consent is shown until the user identifies a community.

Welcome before a community is known

Join a community during onboarding

The relay policy appears after a policy-enabled URL is entered.

Join a community during onboarding

Redeem an invite during onboarding

Consent sits immediately before the redeem action.

Redeem an invite during onboarding

Import an existing key

Key import remains separate from consent until a community is selected.

Import an existing key

Add a community in the app

The shared policy control appears as the final form section before the action.

Add a community in the app

Accept an invite in the browser

The browser invite uses the same two-checkbox treatment and button width.

Accept an invite in the browser

Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
@tlongwell-block
tlongwell-block requested a review from a team as a code owner July 15, 2026 02:19
klopez4212 and others added 3 commits July 15, 2026 09:37
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
@tlongwell-block tlongwell-block changed the title Gate relay invites on optional terms acceptance Apply optional relay join policy across join flows Jul 15, 2026
npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 added 4 commits July 15, 2026 12:18
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
* origin/main: (21 commits)
  refactor(desktop): unify page/section header hierarchy across Agents and Settings (#1912)
  chore(release): release Buzz Desktop version 0.4.6 (#1911)
  fix(desktop): restore chat-first agent creation after mesh rebase (#1909)
  feat(relay): add community unarchive endpoint (#1908)
  Polish agent and team sharing and snapshots (#1852)
  fix(buzz-acp): distinguish cancel-drain expiry from real hard-cap timeout (#1893)
  fix(desktop): filter punctuation from avatar initials (#1904)
  fix(desktop): preserve snapshot attachment URLs (#1905)
  Chat-first agent creation via portable buzz CLI drafts (#1878)
  feat(desktop): enable community rail by default (#1902)
  feat(desktop): redesign Nostr bind verification flow (#1850)
  fix(desktop): simplify sign out settings row (#1903)
  chore(deps): update rust crate reqwest to v0.13.4 (#1792)
  feat(relay): gate Git web GUI separately (#1901)
  feat(desktop): Send feedback modal + profile presence chip (#1756)
  fix(desktop): seed timeline virtualization row heights (#1887)
  fix(desktop): dedupe react-dismissable-layer to stop modal menu → dialog freezes (#1899)
  fix(desktop): clean up Remind me later dialog footer and loading state (#1898)
  fix(desktop): let sidebar action card description wrap over multiple lines (#1891)
  mesh: upgrade runtime, enforce membership, add shared compute provider (#1656)
  ...

Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
@tlongwell-block
tlongwell-block force-pushed the wren/invite-terms-gate branch from da02f98 to a58fb06 Compare July 15, 2026 21:49
npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 and others added 6 commits July 15, 2026 18:19
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
The ToS/Privacy links on join surfaces opened an in-app dialog that
rendered the policy Markdown with the message Markdown component, which
requires the router. During onboarding the router is not mounted, so
clicking either link tore down the whole React tree (blank app).

Fix the class, not the symptom:

- Relay: new public GET /api/join-policy/terms and /privacy routes
  render the operator-configured Markdown as minimal standalone HTML
  pages (pulldown-cmark; raw HTML in the Markdown is escaped to text;
  404 when unconfigured, matching the JSON policy endpoint).
- Desktop: JoinPolicyNotice links now openUrl() those pages in the
  system browser via the Tauri opener; the in-app dialog is deleted.
  Every call site threads relayWsUrl so links resolve against the
  relay that served the policy.

The web invite page keeps its in-page dialog — it already runs in a
browser and does not crash.

Tests: render/escape unit test + Postgres-gated route test on the
relay; joinPolicyDocumentUrl unit test on desktop.

Co-authored-by: Tyler Longwell <tlongwell@squareup.com>
Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
Co-authored-by: Tyler Longwell <tlongwell@squareup.com>
Signed-off-by: Tyler Longwell <tlongwell@squareup.com>
* origin/main:
  feat(desktop): return nostr binding proof in browser fragment (#1933)
  Use selected relay for desktop pairing socket (#1934)
  fix(desktop): allow editing built-in agents (#1928)
  fix(desktop): load downloaded mesh runtime on signed macOS builds (#1932)
  feat(media): require auth for relay media reads (#1926)
  fix(desktop): remove superseded built-in agents (#1929)
  Move agent AI defaults into Settings (#1919)
  feat(desktop): add Honey and Bumble starter agents (#1925)
  feat(desktop): expand owned team mentions (#1918)
  Fix live agent reactions in Inbox and channels (#1921)

Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>

# Conflicts:
#	desktop/src/testing/e2eBridge.ts
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
klopez4212 added a commit that referenced this pull request Jul 16, 2026
npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 added 5 commits July 16, 2026 12:46
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
* origin/main: (29 commits)
  chore(release): release Buzz Mobile version 0.4.5 (#1965)
  fix(mobile): support open pairing relays (#1939)
  feat(desktop): refine Projects navigation and overview hierarchy (#1956)
  Log handoff token counts (#1954)
  fix(onboarding): add Welcome finalize skip escape hatch (#1960)
  Welcome channel: Browse channels card, browser sorting, shortcut hint placement (#1948)
  chore(release): release Buzz Desktop version 0.4.7 (#1955)
  feat(desktop): confirm invites with an Opening-invite loading gate before machine onboarding (#1950)
  fix(mobile): replace pairing placeholder with Buzz icon (#1952)
  feat(desktop): remove theme selection from onboarding, default to Buzz + System (#1947)
  fix(desktop): sentence-case runtime descriptions in onboarding setup (#1944)
  feat(desktop): Projects v3 — overview redesign, activity feed, agent access & sync controls (#1851)
  Rework first-launch community onboarding (#1936)
  fix(desktop): stop marking users away while active on the machine (#1942)
  Add pre-push branch-skew guard and changed-spec e2e recipe (#1940)
  ci: build the relay once and cache it by source hash (#1941)
  fix(desktop): variant-aware connecting gate for first-run handoffs (#1938)
  Redesign agent AI configuration and defaults (#1935)
  Add Buzz-managed Node runtime for agent installs (#1930)
  feat(desktop): return nostr binding proof in browser fragment (#1933)
  ...

# Conflicts:
#	desktop/src/features/communities/ui/AddCommunityDialog.tsx
#	desktop/src/features/communities/ui/WelcomeSetup.tsx
#	desktop/src/features/onboarding/ui/MembershipDenied.tsx
#	desktop/src/shared/deep-link.ts
#	desktop/tests/e2e/onboarding.spec.ts

Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
* origin/wren/invite-terms-gate:
  Polish join policy consent across join flows
  test(relay): assert join policy defaults off
  test(desktop): cover external join-policy links
  Open join-policy documents in the system browser
  Update migration repair test for acceptance migration
  Gate invite acceptance on join policy

# Conflicts:
#	crates/buzz-relay/src/api/invites.rs
#	crates/buzz-relay/src/router.rs
#	desktop/src/app/App.tsx
#	desktop/src/features/communities/ui/AddCommunityDialog.tsx
#	desktop/src/features/communities/ui/WelcomeSetup.tsx
#	desktop/src/features/onboarding/ui/MembershipDenied.tsx
#	desktop/src/shared/api/invites.test.mjs
#	desktop/src/shared/deep-link.ts
#	desktop/tests/e2e/onboarding.spec.ts
#	web/src/features/invite/ui/InvitePage.tsx

Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
@tlongwell-block
tlongwell-block force-pushed the wren/invite-terms-gate branch from b43639c to 19274c0 Compare July 16, 2026 17:00
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
@tlongwell-block
tlongwell-block merged commit 6c2d667 into main Jul 16, 2026
32 checks passed
@tlongwell-block
tlongwell-block deleted the wren/invite-terms-gate branch July 16, 2026 17:59
tlongwell-block pushed a commit that referenced this pull request Jul 16, 2026
* origin/main:
  Apply optional relay join policy across join flows (#1894)
  fix(desktop): preserve relaunch through mesh shutdown (#1966)
atishpatel added a commit that referenced this pull request Jul 16, 2026
…adata

* origin/main:
  fix(join-policy): require legal consent on hosted invites (#1987)
  [codex] Prevent actor-tag UI impersonation (#1931)
  chore(release): release Buzz Desktop version 0.4.9 (#1986)
  Restyle onboarding: branded landing screen, yellow/gradient backgrounds, new starter avatars (#1982)
  feat(acp): retry initial relay connection with terminal/transient error classification (#1922)
  feat(mobile): add external release signing mode for central APK Signer pipelines (#1972)
  Guide CLI installation and subscription sign-in (#1980)
  Scope relay runtime state by community (#1658)
  unify channel add + search into one entry point (#1964)
  Apply optional relay join policy across join flows (#1894)
  fix(desktop): preserve relaunch through mesh shutdown (#1966)
  Persist agent audiences with native inline mentions (#1949)
wpfleger96 added a commit that referenced this pull request Aug 8, 2026
## Buzz Relay release v0.2.1

### Changes since relay-v0.2.0:

- fix(sdk): preserve self-mention p tags in message and forum event
builders ([#4975](#4975))
([`78c87ae20e`](78c87ae))
- feat(desktop): adding rich link previews to messages
([#3818](#3818))
([`1922d49cb2`](1922d49))
- feat(relay): accept kind:30179 private managed-agent events at ingest
([#5133](#5133))
([`ad923353a2`](ad92335))
- fix(media): require authenticated reads
([#4610](#4610))
([`769ac70b74`](769ac70))
- feat(identity): recover desktop identity from a signed-in phone
([#4845](#4845))
([`6eb65919f1`](6eb6591))
- ci: prove the relay-driven mesh lifecycle — discover, join, infer,
deny — with real nodes
([#3862](#3862))
([`38bf642fcf`](38bf642))
- relay: fuzz WebSocket 1012 restart-close timing on graceful drain
(BUZZ_DRAIN_JITTER_MS)
([#4542](#4542))
([`e14fff74d0`](e14fff7))
- fix(reactions): support max-length custom emoji
([#3833](#3833))
([`2ea9385015`](2ea9385))
- fix(channels): restrict private-channel invitations
([#4612](#4612))
([`efe1893dd3`](efe1893))
- fix(workflow): bind trigger author to the signed event
([#4607](#4607))
([`885bed35ee`](885bed3))
- fix(git): revoke access for banned relay members
([#4608](#4608))
([`997b8caaa4`](997b8ca))
- Define private managed agent wire protocol
([#4593](#4593))
([`067c085f37`](067c085))
- perf(relay): index channel-id lookups and skip trace-only reads
([#4647](#4647))
([`bc9e6528a7`](bc9e652))
- Polish mobile inbox and media flows
([#4512](#4512))
([`feccf4eabc`](feccf4e))
- fix(git): allow deleting the default branch
([#4297](#4297))
([`fc598f5f8d`](fc598f5))
- feat(projects): add buzz projects CLI commands (NIP-MP kind:30621)
([#4020](#4020))
([`b7bb15122e`](b7bb151))
- perf(relay): serve relay-membership checks from the read replica
([#4124](#4124))
([`ac4fa13b8e`](ac4fa13))
- fix(relay): allow open relays to set their NIP-11 workspace icon
(kind:9033) ([#3998](#3998))
([`5765fc74b7`](5765fc7))
- feat(relay): accept kind:30621 multi-repo projects at ingest
([#3171](#3171))
([`cb9701cd30`](cb9701c))
- feat(relay): raise hosted community limit to five
([#3829](#3829))
([`10d5a26414`](10d5a26))
- fix(relay): align NIP-11 max_limit with REQ ceiling
([#3635](#3635))
([`23f0c26b1c`](23f0c26))
- feat(relay): gate kind 30178 team-catalog reads behind the shared tag
([#3358](#3358))
([`114d40d9d3`](114d40d))
- fix(db): isolate usage metrics advisory-lock test on scratch DB
([#3670](#3670))
([`dba97eecd9`](dba97ee))
- perf(presence): reduce heartbeat frequency
([#3783](#3783))
([`bf139e8d0b`](bf139e8))
- feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute
(split 1/2 of #3467) ([#3741](#3741))
([`4933672eb4`](4933672))
- feat(replica): portable heartbeat-token fence with snapshot-local
reader routing ([#3268](#3268))
([`63496cc1d4`](63496cc))
- fix(git): channel binding tooling + author remediation for unbound
repos ([#3626](#3626))
([`788b3c002b`](788b3c0))
- feat: configure S3 URL addressing style
([#3400](#3400))
([`7012d86d52`](7012d86))
- feat(tracing): correlate trace IDs in relay logs
([#3608](#3608))
([`005b5b819a`](005b5b8))
- fix(relay): avoid subscription lock inversion
([#3413](#3413))
([`22be8bb351`](22be8bb))
- feat(cli): add users set-status command for NIP-38 profile status
([#3253](#3253))
([`60158fce3e`](60158fc))
- feat(relay): make Postgres pool size configurable, default 50
([#3191](#3191))
([`2ce2d71cc3`](2ce2d71))
- feat(tracing): add datastore tracing plumbing
([#2760](#2760))
([`e94b9aeda0`](e94b9ae))
- feat(invites): add use-limited invite links
([#3141](#3141))
([`d500c2d5cf`](d500c2d))
- feat(admin): show reported message content in report detail
([#3149](#3149))
([`f069a85503`](f069a85))
- resolve findings ([#3150](#3150))
([`9b0f744804`](9b0f744))
- Revert "fix(cli,relay): resolve agents by verified owner"
([#3168](#3168))
([`a041e2d21e`](a041e2d))
- fix(cli,relay): resolve agents by verified owner
([#2615](#2615))
([`c3084b36d9`](c3084b3))
- fix(security): enforce durable community ban on NIP-43 relay-admin
kinds 9030-9033 ([#3128](#3128))
([`e2e0079101`](e2e0079))
- fix(security): authorize kind:9000 role changes in both directions
([#3017](#3017))
([`00ecf2cac7`](00ecf2c))
- feat(desktop): handle project work from Inbox
([#3117](#3117))
([`c5c4f390b6`](c5c4f39))
- feat(relay): make per-owner community limit configurable via
BUZZ_MAX_COMMUNITIES_PER_OWNER
([#2599](#2599))
([`2a051a404d`](2a051a4))
- feat(relay): add author-only-unless-shared read gate for kind 30175
([#2768](#2768))
([`ab3af82871`](ab3af82))
- fix(core): block IPv6 transition SSRF targets
([#2801](#2801))
([`c26bf5945d`](c26bf59))
- fix(workflow): bypass system proxies for webhooks
([#2800](#2800))
([`60a171b19e`](60a171b))
- fix(audit): hash created_at at the precision Postgres stores
([#2638](#2638))
([`264a56a226`](264a56a))
- feat(desktop): make pull request reviews actionable
([#2510](#2510))
([`9081ab0ec9`](9081ab0))
- fix(relay): decompress gzip-encoded git smart-HTTP request bodies
([#2670](#2670))
([`5ca36e7b91`](5ca36e7))
- fix(sharing): preserve agent/team snapshot tEXt chunks through media
sanitization ([#2438](#2438))
([`b096b0a15a`](b096b0a))
- fix(relay): send 1012 restart close to all clients on graceful drain
([#2575](#2575))
([`1911c69aa2`](1911c69))
- fix(media): sanitize animated image uploads
([#2524](#2524))
([`8f8f5fa5a4`](8f8f5fa))
- fix(channels): strip leading hash prefixes from names
([#2250](#2250))
([`d0ab3fdb05`](d0ab3fd))
- feat(relay): make Redis pool size configurable, default 16
([#2521](#2521))
([`bcc3e13069`](bcc3e13))
- feat(desktop+acp): spawn a harness per (agent, community) pair at GUI
startup — warm sockets, lazy LLM pool
([#2122](#2122))
([`61cc738ee8`](61cc738))
- feat(media): add S3-truth per-community storage sweep
([#2044](#2044))
([`bd37a4d584`](bd37a4d))
- feat(relay): log NIP-98 pubkey attribution on HTTP bridge requests
([#2206](#2206))
([`7e34bee62c`](7e34bee))
- Revert "feat(relay): inventory unreachable Git objects"
([#2275](#2275))
([`0fb820f9bf`](0fb820f))
- feat(relay): inventory unreachable Git objects
([#2264](#2264))
([`3afc9dae15`](3afc9da))
- relay: add author_type label to buzz_events_stored_total
([#2243](#2243))
([`b9f54c43fe`](b9f54c4))
- fix(git): make project branch workflows reliable
([#2213](#2213))
([`166f27be4b`](166f27b))
- feat(cli): manage repository protection rules
([#2193](#2193))
([`f94324598d`](f943245))
- feat(cli): add agents archive/unarchive/archived subcommands
([#2173](#2173))
([`7d7992067b`](7d79920))
- fix(mobile): sanitize Android image uploads
([#2188](#2188))
([`ee21da90bd`](ee21da9))
- fix(cli): paginate channel directory queries
([#2181](#2181))
([`03fe19d603`](03fe19d))
- fix(mobile): image upload fails due to unstripped metadata
([#2185](#2185))
([`37f15b2001`](37f15b2))
- perf(relay): compact Git packs before manifest limits
([#2172](#2172))
([`80e0ab16b0`](80e0ab1))
- perf(relay): cache Git pack hydration
([#2169](#2169))
([`a4d82ec722`](a4d82ec))
- fix(relay): bound and observe Git read operations
([#2167](#2167))
([`5f7c93d9c1`](5f7c93d))
- relay: gate push enqueue on live leases; batch matcher pipeline
(T1b/T1a-repair/T2b) ([#2145](#2145))
([`e43b2d5aac`](e43b2d5))
- relay: add audit logging disable switch
([#2134](#2134))
([`bf5acabdde`](bf5acab))
- relay: skip TTL deadline bump for known-permanent channels (T1a
write-amp) ([#2125](#2125))
([`2e936d439c`](2e936d4))
- fix(git): carry NIP-OA delegation in auth event
([#2120](#2120))
([`c12257d57a`](c12257d))
- Route lag-tolerant reads to an optional Postgres read replica
([#2084](#2084))
([`29c48883d3`](29c4888))
- fix: recover community access visibility
([#2074](#2074))
([`ca384d082d`](ca384d0))
- feat: proxy feedback-scoped admin attachments
([#2059](#2059))
([`d7f918e3cb`](d7f918e))
- feat: add read-only deployment moderation dashboard
([#1999](#1999))
([`68e670e001`](68e670e))
- Bug-bash round 2: table scroll, Goose instructions, workflow mention
wake ([#2034](#2034))
([`64b8fea6dc`](64b8fea))
- Strip media metadata on clients and reject it at the relay
([#2006](#2006))
([`5cfd69cb0c`](5cfd69c))
- [codex] Hold Git concurrency permits through streaming (BUZZ-SEC-018)
([#1916](#1916))
([`7baea42abb`](7baea42))
- [codex] Enforce shared relay admission limits (BUZZ-SEC-019)
([#1917](#1917))
([`73fc0ec6cf`](73fc0ec))
- [codex] Block banned actors from moderation commands (BUZZ-SEC-007)
([#1915](#1915))
([`caa195ca58`](caa195c))
- [codex] Fix relay WebSocket admission limits
([#1682](#1682))
([`d3ce971fc7`](d3ce971))
- feat: add invite QR and mobile direct join
([#1957](#1957))
([`648cbf3610`](648cbf3))
- fix(join-policy): require legal consent on hosted invites
([#1987](#1987))
([`2e1577f76f`](2e1577f))
- [codex] Prevent actor-tag UI impersonation
([#1931](#1931))
([`c540ec9678`](c540ec9))
- Scope relay runtime state by community
([#1658](#1658))
([`d52dedb06f`](d52dedb))
- Apply optional relay join policy across join flows
([#1894](#1894))
([`6c2d667575`](6c2d667))
- feat(media): require auth for relay media reads
([#1926](#1926))
([`f308762852`](f308762))
- feat(relay): add community unarchive endpoint
([#1908](#1908))
([`6b9641db2b`](6b9641d))
- feat(relay): gate Git web GUI separately
([#1901](#1901))
([`34dc7dec75`](34dc7de))
- mesh: upgrade runtime, enforce membership, add shared compute provider
([#1656](#1656))
([`54638ff4bb`](54638ff))
- Route Git scratch through configured volume
([#1884](#1884))
([`2318b3096c`](2318b30))
- feat(relay): gate usage metrics behind stable leader
([#1814](#1814))
([`59e9821503`](59e9821))
- Relay mesh: cross-pod tunnel + huddle transport (buzz-relay-mesh)
([#1670](#1670))
([`ccb021d713`](ccb021d))
- feat(push): deliver accepted relay events as wakes
([#1866](#1866))
([`bffbc5f22c`](bffbc5f))
- fix(db): resolve duplicate migration version
([#1863](#1863))
([`08ad38a07f`](08ad38a))
- Add private product feedback sidecar
([#1857](#1857))
([`af190c93e1`](af190c9))
- feat(relay): add durable community archival
([#1834](#1834))
([`2b15a72675`](2b15a72))
- feat(push): add public APNs gateway
([#1770](#1770))
([`1c006822e4`](1c00682))
- feat(relay): add atomic community ownership transfer
([#1845](#1845))
([`52e42ccb9f`](52e42cc))
- Bound NIP-RS retention and search indexing
([#1771](#1771))
([`1b4703021d`](1b47030))
- Add optional standalone pairing relay to Helm chart
([#1799](#1799))
([`9b47c8548f`](9b47c85))
- fix(relay): publish membership snapshot on provisioning
([#1761](#1761))
([`0950d392b7`](0950d39))
- feat(relay): per-community usage metrics
([#1723](#1723))
([`620822899a`](6208228))
- refactor(desktop): remove vestigial MCP toolsets config
([#1776](#1776))
([`dfec75b3c0`](dfec75b))

**To release:** merge this PR. The tag and build will happen
automatically.

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
41fred pushed a commit to 41fred/buzz that referenced this pull request Aug 9, 2026
## Buzz Relay release v0.2.1

### Changes since relay-v0.2.0:

- fix(sdk): preserve self-mention p tags in message and forum event
builders ([block#4975](block#4975))
([`78c87ae20e`](block@78c87ae))
- feat(desktop): adding rich link previews to messages
([block#3818](block#3818))
([`1922d49cb2`](block@1922d49))
- feat(relay): accept kind:30179 private managed-agent events at ingest
([block#5133](block#5133))
([`ad923353a2`](block@ad92335))
- fix(media): require authenticated reads
([block#4610](block#4610))
([`769ac70b74`](block@769ac70))
- feat(identity): recover desktop identity from a signed-in phone
([block#4845](block#4845))
([`6eb65919f1`](block@6eb6591))
- ci: prove the relay-driven mesh lifecycle — discover, join, infer,
deny — with real nodes
([block#3862](block#3862))
([`38bf642fcf`](block@38bf642))
- relay: fuzz WebSocket 1012 restart-close timing on graceful drain
(BUZZ_DRAIN_JITTER_MS)
([block#4542](block#4542))
([`e14fff74d0`](block@e14fff7))
- fix(reactions): support max-length custom emoji
([block#3833](block#3833))
([`2ea9385015`](block@2ea9385))
- fix(channels): restrict private-channel invitations
([block#4612](block#4612))
([`efe1893dd3`](block@efe1893))
- fix(workflow): bind trigger author to the signed event
([block#4607](block#4607))
([`885bed35ee`](block@885bed3))
- fix(git): revoke access for banned relay members
([block#4608](block#4608))
([`997b8caaa4`](block@997b8ca))
- Define private managed agent wire protocol
([block#4593](block#4593))
([`067c085f37`](block@067c085))
- perf(relay): index channel-id lookups and skip trace-only reads
([block#4647](block#4647))
([`bc9e6528a7`](block@bc9e652))
- Polish mobile inbox and media flows
([block#4512](block#4512))
([`feccf4eabc`](block@feccf4e))
- fix(git): allow deleting the default branch
([block#4297](block#4297))
([`fc598f5f8d`](block@fc598f5))
- feat(projects): add buzz projects CLI commands (NIP-MP kind:30621)
([block#4020](block#4020))
([`b7bb15122e`](block@b7bb151))
- perf(relay): serve relay-membership checks from the read replica
([block#4124](block#4124))
([`ac4fa13b8e`](block@ac4fa13))
- fix(relay): allow open relays to set their NIP-11 workspace icon
(kind:9033) ([block#3998](block#3998))
([`5765fc74b7`](block@5765fc7))
- feat(relay): accept kind:30621 multi-repo projects at ingest
([block#3171](block#3171))
([`cb9701cd30`](block@cb9701c))
- feat(relay): raise hosted community limit to five
([block#3829](block#3829))
([`10d5a26414`](block@10d5a26))
- fix(relay): align NIP-11 max_limit with REQ ceiling
([block#3635](block#3635))
([`23f0c26b1c`](block@23f0c26))
- feat(relay): gate kind 30178 team-catalog reads behind the shared tag
([block#3358](block#3358))
([`114d40d9d3`](block@114d40d))
- fix(db): isolate usage metrics advisory-lock test on scratch DB
([block#3670](block#3670))
([`dba97eecd9`](block@dba97ee))
- perf(presence): reduce heartbeat frequency
([block#3783](block#3783))
([`bf139e8d0b`](block@bf139e8))
- feat(mesh): upgrade embedded mesh to v0.74 and harden shared compute
(split 1/2 of block#3467) ([block#3741](block#3741))
([`4933672eb4`](block@4933672))
- feat(replica): portable heartbeat-token fence with snapshot-local
reader routing ([block#3268](block#3268))
([`63496cc1d4`](block@63496cc))
- fix(git): channel binding tooling + author remediation for unbound
repos ([block#3626](block#3626))
([`788b3c002b`](block@788b3c0))
- feat: configure S3 URL addressing style
([block#3400](block#3400))
([`7012d86d52`](block@7012d86))
- feat(tracing): correlate trace IDs in relay logs
([block#3608](block#3608))
([`005b5b819a`](block@005b5b8))
- fix(relay): avoid subscription lock inversion
([block#3413](block#3413))
([`22be8bb351`](block@22be8bb))
- feat(cli): add users set-status command for NIP-38 profile status
([block#3253](block#3253))
([`60158fce3e`](block@60158fc))
- feat(relay): make Postgres pool size configurable, default 50
([block#3191](block#3191))
([`2ce2d71cc3`](block@2ce2d71))
- feat(tracing): add datastore tracing plumbing
([block#2760](block#2760))
([`e94b9aeda0`](block@e94b9ae))
- feat(invites): add use-limited invite links
([block#3141](block#3141))
([`d500c2d5cf`](block@d500c2d))
- feat(admin): show reported message content in report detail
([block#3149](block#3149))
([`f069a85503`](block@f069a85))
- resolve findings ([block#3150](block#3150))
([`9b0f744804`](block@9b0f744))
- Revert "fix(cli,relay): resolve agents by verified owner"
([block#3168](block#3168))
([`a041e2d21e`](block@a041e2d))
- fix(cli,relay): resolve agents by verified owner
([block#2615](block#2615))
([`c3084b36d9`](block@c3084b3))
- fix(security): enforce durable community ban on NIP-43 relay-admin
kinds 9030-9033 ([block#3128](block#3128))
([`e2e0079101`](block@e2e0079))
- fix(security): authorize kind:9000 role changes in both directions
([block#3017](block#3017))
([`00ecf2cac7`](block@00ecf2c))
- feat(desktop): handle project work from Inbox
([block#3117](block#3117))
([`c5c4f390b6`](block@c5c4f39))
- feat(relay): make per-owner community limit configurable via
BUZZ_MAX_COMMUNITIES_PER_OWNER
([block#2599](block#2599))
([`2a051a404d`](block@2a051a4))
- feat(relay): add author-only-unless-shared read gate for kind 30175
([block#2768](block#2768))
([`ab3af82871`](block@ab3af82))
- fix(core): block IPv6 transition SSRF targets
([block#2801](block#2801))
([`c26bf5945d`](block@c26bf59))
- fix(workflow): bypass system proxies for webhooks
([block#2800](block#2800))
([`60a171b19e`](block@60a171b))
- fix(audit): hash created_at at the precision Postgres stores
([block#2638](block#2638))
([`264a56a226`](block@264a56a))
- feat(desktop): make pull request reviews actionable
([block#2510](block#2510))
([`9081ab0ec9`](block@9081ab0))
- fix(relay): decompress gzip-encoded git smart-HTTP request bodies
([block#2670](block#2670))
([`5ca36e7b91`](block@5ca36e7))
- fix(sharing): preserve agent/team snapshot tEXt chunks through media
sanitization ([block#2438](block#2438))
([`b096b0a15a`](block@b096b0a))
- fix(relay): send 1012 restart close to all clients on graceful drain
([block#2575](block#2575))
([`1911c69aa2`](block@1911c69))
- fix(media): sanitize animated image uploads
([block#2524](block#2524))
([`8f8f5fa5a4`](block@8f8f5fa))
- fix(channels): strip leading hash prefixes from names
([block#2250](block#2250))
([`d0ab3fdb05`](block@d0ab3fd))
- feat(relay): make Redis pool size configurable, default 16
([block#2521](block#2521))
([`bcc3e13069`](block@bcc3e13))
- feat(desktop+acp): spawn a harness per (agent, community) pair at GUI
startup — warm sockets, lazy LLM pool
([block#2122](block#2122))
([`61cc738ee8`](block@61cc738))
- feat(media): add S3-truth per-community storage sweep
([block#2044](block#2044))
([`bd37a4d584`](block@bd37a4d))
- feat(relay): log NIP-98 pubkey attribution on HTTP bridge requests
([block#2206](block#2206))
([`7e34bee62c`](block@7e34bee))
- Revert "feat(relay): inventory unreachable Git objects"
([block#2275](block#2275))
([`0fb820f9bf`](block@0fb820f))
- feat(relay): inventory unreachable Git objects
([block#2264](block#2264))
([`3afc9dae15`](block@3afc9da))
- relay: add author_type label to buzz_events_stored_total
([block#2243](block#2243))
([`b9f54c43fe`](block@b9f54c4))
- fix(git): make project branch workflows reliable
([block#2213](block#2213))
([`166f27be4b`](block@166f27b))
- feat(cli): manage repository protection rules
([block#2193](block#2193))
([`f94324598d`](block@f943245))
- feat(cli): add agents archive/unarchive/archived subcommands
([block#2173](block#2173))
([`7d7992067b`](block@7d79920))
- fix(mobile): sanitize Android image uploads
([block#2188](block#2188))
([`ee21da90bd`](block@ee21da9))
- fix(cli): paginate channel directory queries
([block#2181](block#2181))
([`03fe19d603`](block@03fe19d))
- fix(mobile): image upload fails due to unstripped metadata
([block#2185](block#2185))
([`37f15b2001`](block@37f15b2))
- perf(relay): compact Git packs before manifest limits
([block#2172](block#2172))
([`80e0ab16b0`](block@80e0ab1))
- perf(relay): cache Git pack hydration
([block#2169](block#2169))
([`a4d82ec722`](block@a4d82ec))
- fix(relay): bound and observe Git read operations
([block#2167](block#2167))
([`5f7c93d9c1`](block@5f7c93d))
- relay: gate push enqueue on live leases; batch matcher pipeline
(T1b/T1a-repair/T2b) ([block#2145](block#2145))
([`e43b2d5aac`](block@e43b2d5))
- relay: add audit logging disable switch
([block#2134](block#2134))
([`bf5acabdde`](block@bf5acab))
- relay: skip TTL deadline bump for known-permanent channels (T1a
write-amp) ([block#2125](block#2125))
([`2e936d439c`](block@2e936d4))
- fix(git): carry NIP-OA delegation in auth event
([block#2120](block#2120))
([`c12257d57a`](block@c12257d))
- Route lag-tolerant reads to an optional Postgres read replica
([block#2084](block#2084))
([`29c48883d3`](block@29c4888))
- fix: recover community access visibility
([block#2074](block#2074))
([`ca384d082d`](block@ca384d0))
- feat: proxy feedback-scoped admin attachments
([block#2059](block#2059))
([`d7f918e3cb`](block@d7f918e))
- feat: add read-only deployment moderation dashboard
([block#1999](block#1999))
([`68e670e001`](block@68e670e))
- Bug-bash round 2: table scroll, Goose instructions, workflow mention
wake ([block#2034](block#2034))
([`64b8fea6dc`](block@64b8fea))
- Strip media metadata on clients and reject it at the relay
([block#2006](block#2006))
([`5cfd69cb0c`](block@5cfd69c))
- [codex] Hold Git concurrency permits through streaming (BUZZ-SEC-018)
([block#1916](block#1916))
([`7baea42abb`](block@7baea42))
- [codex] Enforce shared relay admission limits (BUZZ-SEC-019)
([block#1917](block#1917))
([`73fc0ec6cf`](block@73fc0ec))
- [codex] Block banned actors from moderation commands (BUZZ-SEC-007)
([block#1915](block#1915))
([`caa195ca58`](block@caa195c))
- [codex] Fix relay WebSocket admission limits
([block#1682](block#1682))
([`d3ce971fc7`](block@d3ce971))
- feat: add invite QR and mobile direct join
([block#1957](block#1957))
([`648cbf3610`](block@648cbf3))
- fix(join-policy): require legal consent on hosted invites
([block#1987](block#1987))
([`2e1577f76f`](block@2e1577f))
- [codex] Prevent actor-tag UI impersonation
([block#1931](block#1931))
([`c540ec9678`](block@c540ec9))
- Scope relay runtime state by community
([block#1658](block#1658))
([`d52dedb06f`](block@d52dedb))
- Apply optional relay join policy across join flows
([block#1894](block#1894))
([`6c2d667575`](block@6c2d667))
- feat(media): require auth for relay media reads
([block#1926](block#1926))
([`f308762852`](block@f308762))
- feat(relay): add community unarchive endpoint
([block#1908](block#1908))
([`6b9641db2b`](block@6b9641d))
- feat(relay): gate Git web GUI separately
([block#1901](block#1901))
([`34dc7dec75`](block@34dc7de))
- mesh: upgrade runtime, enforce membership, add shared compute provider
([block#1656](block#1656))
([`54638ff4bb`](block@54638ff))
- Route Git scratch through configured volume
([block#1884](block#1884))
([`2318b3096c`](block@2318b30))
- feat(relay): gate usage metrics behind stable leader
([block#1814](block#1814))
([`59e9821503`](block@59e9821))
- Relay mesh: cross-pod tunnel + huddle transport (buzz-relay-mesh)
([block#1670](block#1670))
([`ccb021d713`](block@ccb021d))
- feat(push): deliver accepted relay events as wakes
([block#1866](block#1866))
([`bffbc5f22c`](block@bffbc5f))
- fix(db): resolve duplicate migration version
([block#1863](block#1863))
([`08ad38a07f`](block@08ad38a))
- Add private product feedback sidecar
([block#1857](block#1857))
([`af190c93e1`](block@af190c9))
- feat(relay): add durable community archival
([block#1834](block#1834))
([`2b15a72675`](block@2b15a72))
- feat(push): add public APNs gateway
([block#1770](block#1770))
([`1c006822e4`](block@1c00682))
- feat(relay): add atomic community ownership transfer
([block#1845](block#1845))
([`52e42ccb9f`](block@52e42cc))
- Bound NIP-RS retention and search indexing
([block#1771](block#1771))
([`1b4703021d`](block@1b47030))
- Add optional standalone pairing relay to Helm chart
([block#1799](block#1799))
([`9b47c8548f`](block@9b47c85))
- fix(relay): publish membership snapshot on provisioning
([block#1761](block#1761))
([`0950d392b7`](block@0950d39))
- feat(relay): per-community usage metrics
([block#1723](block#1723))
([`620822899a`](block@6208228))
- refactor(desktop): remove vestigial MCP toolsets config
([block#1776](block#1776))
([`dfec75b3c0`](block@dfec75b))

**To release:** merge this PR. The tag and build will happen
automatically.

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
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.

2 participants