Commit Graph
1470 Commits
Author SHA1 Message Date
Will Pfleger a6330254fc fix(relay): suppress expired drafts at read time with 30-day server TTL
NIP-37 expiration tags were validated at ingest then discarded — expired
drafts were served and counted indefinitely. This addresses R3 from Tyler's
team's retention review.

Adds draft_expired(event, now) to reader_can_receive_event: a KIND_DRAFT
event is suppressed when now >= min(expiration_tag, created_at + 30d).
Tombstones follow the same rule. Non-draft kinds are unaffected.

The COUNT fast-path (author_is_self SQL count_events()) cannot evaluate
per-event expiry, so filter_can_match_draft forces draft-matching COUNT
filters to the per-event fallback on all four fast-path sites (count.rs
×2, bridge.rs ×2). KIND_EVENT_REMINDER retains its fast-path — reminders
carry no expiry semantics.

All changes are rebase-safe against #1771: nothing in replace_parameterized_event
or schema is touched. NIP-40 advertisement stays absent until the physical
delete reaper (#1771 coupling) lands.
2026-07-14 00:21:25 -04:00
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger 6eb6ab1a1e test(search): serialize pgcrypto install to fix parallel-setup race
fts_integration.rs setup() applies migration 0001, which contains
CREATE EXTENSION IF NOT EXISTS pgcrypto.  pg_extension is database-
global with a unique index on extname; IF NOT EXISTS is a check-then-
insert, not an atomic upsert.  When 18 tests run in parallel, the first
wave all see the extension as absent, all attempt the insert, and all
but one hit SQLSTATE 23505 (duplicate key on pg_extension_name_index).

Fix: take a session-level pg_advisory_lock on the admin connection
before the extension create, release it after.  This serializes the
install across all parallel test workers so exactly one does the real
CREATE; the rest treat it as a no-op on lock acquisition.  The advisory
lock is released automatically when the session ends.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-14 00:21:25 -04:00
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger 6dda6fd7c3 test(relay): fix D2/D3 CI-failing e2e tests for draft HTTP query and fanout
D2: rewrite test_draft_not_returned_in_kindless_channel_http_query.
The HTTP bridge runs sensitive-kind gates unconditionally, so a kindless
h-tag filter triggers p_gated_filters_authorized and returns 403 for any
non-owner caller — this is fail-closed, pre-existing main behavior.  The
test now asserts 403 for the kindless case (i), then uses a mixed
kinds:[9,31234] h-tag filter as the attacker (passes all three HTTP
gates) and asserts kind:9 appears while the draft is absent (ii),
proving reader_can_receive_event is live on the bridge per-event path.

D3: add #h ch_id tag to subscription filter in
test_draft_live_fanout_reaches_author_own_subscription.  Drafts are
channel-bound events; fan_out_scoped's symmetric scoping invariant
means a global (no #h) sub never sees channel-scoped events by design.
Channel-scoping the filter routes the sub through channel_kind_index so
fan-out can match and filter_fanout_by_access passes the author through.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-14 00:21:25 -04:00
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger 985c20f885 fix(relay): address pass-1 review findings R1-R3
R1 (IMPORTANT): Extract should_dispatch_workflow helper from inline if
condition in dispatch_persistent_event_inner.  The Q7 test previously
duplicated the production predicate as a local closure; deleting the
AUTHOR_ONLY_KINDS guard from the real if block would leave it green.
Now dispatch_persistent_event_inner calls should_dispatch_workflow, and
the test calls the same function — no copy-paste divergence is possible.
Test gains kind:30300 + is_relay_workflow_msg=true controls.

R2 (IMPORTANT): Add test_reminder_target_reaction_oracle_closed e2e to
prove the author-only mask in derive_reaction_channel generalizes to
kind:30300 reminders (not only kind:31234 drafts).  Corrects the
overclaiming comment at ingest.rs:4149-4154 which asserted the unit
tests exercised actor_can_reference_target directly; they only assert
constant membership, and the comment now says so explicitly with a
pointer to the e2e tests that provide behavioral coverage.

R3 (MINOR): Move draft timestamp to base+3 in
test_channel_window_cursor_boundary_excludes_draft so the draft is
inside the raw first page (raw DESC: msg2@+4, draft@+3, msg1@+2,
msg0@+0 — a filter-after-pagination regression would cursor off draft).
Replace the optional if-let cursor branch with cursor.expect(), forcing
the test to fail if has_more=false or no cursor is returned.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-14 00:21:25 -04:00
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger 4a7ea477ed test(relay): add Q15 e2e oracle-closure tests for write-path draft id guards
The previous Q15 unit tests only asserted AUTHOR_ONLY_KINDS membership;
deleting the guard from derive_reaction_channel or
resolve_nip10_thread_meta left every one green.  Replace the vacuous
coverage with four `#[ignore]` e2e tests that exercise the live relay:

- test_draft_target_reaction_oracle_closed: attacker submits kind:7
  reaction to (A) real draft id, (B) random nonexistent id; asserts
  byte-identical error string and zero kind:7 rows stored.
- test_draft_target_thread_parent_oracle_closed: attacker submits kind:9
  reply with draft id as NIP-10 parent vs random parent; asserts
  byte-identical error, zero stored replies, and no 39005 fan-out.
- test_draft_target_public_reference_author_also_rejected: draft author
  reacts to and replies-to their own draft; both must be rejected with the
  masking not-found errors (public-reference paths reject everyone).
- test_draft_target_kind5_oracle_closed: attacker submits kind:5 e-tag
  deletion targeting (A) real draft id, (B) random id; asserts
  byte-identical masking error and draft still readable by author.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-14 00:21:25 -04:00
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger 5c3ac62860 fix(relay): close write-path id-oracle and complete Q2-Q16 review fixes
Add actor_can_reference_target helper to mask author-only event ids
on all five write-path target-resolution sites (reaction channel
derivation, NIP-10 thread parent, stream-edit, forum-vote, kind:5
e-tag deletion), preventing non-authors from distinguishing a real
draft/reminder id from a random id via differential error responses.

Q2: DbError::DraftChannelRequired + channel_id=None DB-layer rejection
Q3: participates_in_thread_metadata split; outer e-tag rejection on drafts
Q4: reader_can_receive_event canonical read gate in buzz-core/filter.rs;
     wire into req.rs / count.rs / bridge.rs / search path
Q5: CI step for buzz-search FTS integration tests (ignored suite now runs)
Q6: HTTP catchall kindless privacy e2e test_draft_not_returned_in_kindless_channel_http_query
Q7: draft_kind_is_excluded_from_workflow_dispatch_by_author_only_guard
Q8: a-tag defensive-guard comment rewrite in side_effects.rs
Q9: e2e smoke for outer-e-tag rejection (test_draft_rejected_outer_e_tag)
Q10: fan-out author-side positive control (test_draft_live_fanout_reaches_author_own_subscription)
Q11: mixed-kinds /count test (test_draft_attacker_mixed_kinds_count_excludes_drafts)
Q12: derive exclusion list from AUTHOR_ONLY_KINDS in thread.rs
Q13: DM privacy recipient-side test (test_dm_draft_not_readable_by_dm_recipient)
Q14: migration 0007 deploy-window note
Q15: actor_can_reference_target post-lookup helper applied at reaction channel
     derivation (all actors rejected), NIP-10 thread parent (all actors
     rejected), stream-edit / forum-vote (non-author masked, author falls
     through), kind:5 e-tag deletion (non-author masked before authz,
     false comment corrected); generalizes over AUTHOR_ONLY_KINDS so
     kind:30300 reminders are also protected; unit tests confirm membership
Q16: channel-window cursor-boundary e2e (test_channel_window_cursor_boundary_excludes_draft)
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-14 00:21:25 -04:00
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger ed274bdc3c fix(relay): exclude author-only kinds from channel-window path
The bridge top_level channel-window filter (handle_channel_window_filter)
passed all window rows to the response without an author-only guard. Since
next_cursor and has_more are computed at the DB layer before any in-memory
filtering, a bridge-level skip would still expose draft ids via the 39006
bounds overlay and leave has_more counts inflated by draft rows.

Fix: add an author_pubkey param to get_channel_window. When Some, the query
appends AND (e.kind NOT IN (30300, 31234) OR e.pubkey = $N), excluding
author-only kinds (KIND_DRAFT=31234, KIND_EVENT_REMINDER=30300) for rows
whose pubkey does not match the requester. This keeps the cursor, has_more,
and all overlay values computed against the already-restricted row set.

Pass Some(&pubkey_bytes) from handle_channel_window_filter via the new
pubkey_bytes parameter; internal / test callers pass None.

Tests: two new e2e tests in e2e_nip37_draft.rs:
- test_channel_window_draft_excluded_for_non_author: mixed kinds:[9,31234]
  query by a channel member who is not the author must return zero draft
  rows and zero draft ids anywhere in the response (rows, aux, overlays).
  kind:9 positive control row must still be present.
- test_channel_window_draft_visible_to_author: the author sees their own
  kind:31234 draft in the window, consistent with all other author-only
  read paths.

Closes the last unguarded author-only read surface identified in code review
of PR #1757.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-14 00:21:25 -04:00
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger 1e82a573d7 fix(relay): block all e-tag deletion routes targeting kind:31234 drafts
A kind:5 e-tag deletion that resolved to a kind:31234 event was
accepted by validate_standard_deletion_event (which only checked
authorship, not target kind). After soft-delete the live head row was
gone, allowing a second write with a different channel h-tag to bypass
the immutable-binding invariant. That path was fixed in the previous
commit on this branch.

A second bypass existed via kind:9005 (channel admin delete): the 9005
branch in validate_admin_event resolved the e-tag target and authorized
the actor (including channel admins who do not own the draft), then
returned Ok(()). Post-storage side effects would soft-delete the head
row, re-opening the cross-channel rebind window.

Fix: in the kind:9005 branch of validate_admin_event (side_effects.rs),
immediately after target resolution, reject pre-storage if the target
kind is KIND_DRAFT. Authorship and agent-owner actors receive the
tombstone-guidance error (mirror of the kind:5 wording). All other
actors — including channel admins — receive the generic
"target event not found" response, byte-identical to the missing-target
branch, so the validator cannot act as a draft-existence oracle.

Add two new E2E regressions:
- test_nip09_kind9005_deletion_of_draft_is_rejected_and_binding_holds:
  full bypass sequence (publish draft h=A -> kind:9005 e=draft rejected
  -> head still live -> h=B rebind rejected), deterministic base/base+1
  timestamps.
- test_nip09_kind9005_admin_deletion_of_draft_is_masked_as_not_found:
  channel admin submits kind:9005 targeting a draft; verifies response
  is exactly "target event not found" (oracle-masking tripwire).

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-14 00:21:25 -04:00
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger edd16089db test(relay): fix test_draft_rejected_p_tag — use third-party pubkey in p tag
EventBuilder silently drops p tags whose value equals the signer's own
pubkey (NIP self-tagging suppression, enabled by default). The test was
using owner.public_key().to_hex() as the p tag value, so the tag was
stripped before signing and the event arrived at the relay with no p tag
at all — the validator always saw a clean envelope and the rejection was
never exercised.

Fix: generate a separate Keys pair for the p tag value so it survives
EventBuilder's self-tag filter and reaches the relay's validator.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-14 00:21:25 -04:00
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger 6fcb8a723d test(relay): harden NIP-37 coverage matrix — non-vacuous tests, Clippy clean
Fix all remaining quality gaps identified in the pre-Thufir review:

Clippy (FIX-11):
- Remove 17 needless borrows in e2e_nip37_draft.rs (auto-fixed)
- sort_by → sort_by_key in tie-break test (auto-fixed)
- while_let_loop → while let loop in removed-member fan-out test
- splitn(3, ':').next() → split(':').next() in ingest.rs NIP-09 guard

DB tests (CRITICAL-A-test, FIX-4, FIX-5):
- Add build_test_draft_at helper (explicit timestamp control)
- Add query_draft_head helper (reusable across tests)
- Expand draft_is_confined_to_its_community: full A/B lifecycle (insert →
  query → replace → tombstone) with scoped head assertions after each step;
  uses same d_tag in both communities to prove community_id is the real
  isolation boundary
- Add draft_channel_binding_is_immutable_across_sequential_calls: sequential
  rebind attempt on an already-bound address → DraftChannelMismatch; stored
  head still v1 after failed rebind
- Add post-race head query to concurrent_different_channel_drafts_one_wins_one_loses:
  assert exactly one live head bound to the winning channel after the race

E2E tests (CRITICAL-B-test, FIX-6, FIX-7, FIX-8, FIX-9, FIX-10):
- Add test_nip09_a_tag_deletion_of_draft_is_rejected: kind:5 a-tag targeting
  31234:<pubkey>:<d> must be rejected; draft must still be live head after
- FIX-7: Expand workflow tripwire to evaluate the actual dispatch predicate
  (is_workflow_execution_kind && is_command_kind && AUTHOR_ONLY_KINDS) for
  kind:31234 (→ false) and kind:9 (→ true, positive control)
- FIX-8: DM test — replace silent return with panic! on missing channel_id;
  use strictly increasing timestamps (base-2, base-1, base) to guarantee
  deterministic ordering across v1/v2/tombstone
- FIX-6: test_draft_not_returned_in_kindless_channel_query — rewrite to use
  attacker (not owner) as requester; the author-only gate strips drafts from
  non-author queries, not from the author's own channel queries
- FIX-9: Removed-member live fan-out — use author(owner) subscription so the
  probe event (owner draft) actually matches the filter and exercises the gate
- FIX-10: Rename test_draft_tenant_confinement_channel_from_different_community
  → inline note + pointer to the existing test_draft_rejected_nonexistent_channel_h_tag
  (the old name was misleading; true cross-tenant confinement is the DB test)

CI (FIX-CI):
- Wire buzz-db NIP-37 draft Postgres tests to backend-integration job

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-14 00:21:25 -04:00
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger e3a0e257bc test(relay): add non-canonical UUID h-tag rejection tests; drop dead get_draft_head_channel_id
validate_draft_wrap_envelope already had the parsed.to_string() != h guard
(hardening commit 2f27d7c). Add two explicit unit tests exercising:
- uppercase UUID form (parse-valid, non-canonical)
- simple 32-hex form without hyphens (parse-valid, non-canonical)

Both tests confirm the relay rejects these forms with a diagnostic message
mentioning 'lowercase', 'canonical', or 'UUID'.

Also removes the now-dead get_draft_head_channel_id function from
buzz-db (event.rs + lib.rs wrapper). The preflight call was replaced by
the atomic binding check inside replace_parameterized_event; no callers
remain outside buzz-db itself.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-14 00:21:25 -04:00
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger 268ea028fa fix(relay): harden NIP-37 draft-wrap contract
- Move immutable-channel binding check inside replace_parameterized_event
  under the advisory lock (atomically safe, race-proof). Remove the
  preflight get_draft_head_channel_id call. Add DraftChannelMismatch
  error variant. All other replace_parameterized_event callers pass None.

- Move validate_draft_wrap_envelope before channel extraction so that
  structural failures (missing/duplicate/non-UUID h-tag, p-tag) report
  via the right gate rather than the channel-scope gate.

- Require canonical lowercase-hyphenated UUID in h-tag validator
  (parsed.to_string() == h); reject uppercase and simple-hex forms.

- Fix test_draft_same_second_tie_break: add per-candidate _tiebreak tag
  to force distinct event hashes and non-empty candidate set.

- Replace two timing-prone kindless WS privacy tests with explicit
  kinds=[0,31234] and kinds=[30023,31234] mixed-kinds tests.

- FTS test: use explicit kinds=[1,31234] search filter as author.

- excluded_kinds_are_storage_level_unsearchable: add kind:31234 row,
  update event count and forbidden list.

- Add Postgres DB integration tests: draft_is_confined_to_its_community
  (two-community tenant confinement) and
  concurrent_different_channel_drafts_one_wins_one_loses (race guard).

- Add workflow-dispatch tripwire unit test in event.rs confirming
  AUTHOR_ONLY_KINDS.contains(&KIND_DRAFT) at the guard seam.

- Add DM channel path test (kind:41010 draft acceptance/replacement/
  tombstone) and removed-member read-denial test (historical REQ/COUNT
  + live fan-out denial after removal).

- Fix stale-write test to assert accepted:true result before head check.

- Update stale 'channel-less/global' docs in kind.rs, ingest.rs,
  event.rs to reflect channel-bound reality.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-14 00:21:25 -04:00
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger 49fce89952 feat(relay): rework NIP-37 drafts to channel-bound contract
Draft wraps (kind:31234) now require exactly one `h` UUID tag binding
them to a specific Buzz channel or DM. The relay enforces:

- Exactly one valid UUID `h` tag on every kind:31234 event
- Channel existence: the `h` UUID must resolve to a live channel
- Membership: author must be a member of that channel at write time
- Immutable binding: once a (author, d_tag) draft is written to
  channel A, replacement events must carry the same h=A; rebinding
  to a different channel is rejected at the ingest layer

The previous channel-less/global-state design is removed. Draft fan-out
already applied the author-only gate (AUTHOR_ONLY_KINDS); with channel_id
now non-NULL for kind:31234, the existing channel visibility/membership
filter in fan-out applies naturally with no additional changes.

E2E test suite rewritten for the channel-bound contract:
- h-tag validation: missing, duplicate, non-UUID, nonexistent channel
- Non-member author rejection + removed-member regression
- Immutable binding: rebind rejected, same-channel replacement accepted
- Author-only reads: WS REQ/COUNT, HTTP /query, /count, live fan-out
- known-#d privacy tripwires (exclusive and kindless)
- Tombstone head queryable by author, tombstone replaces live draft
- NIP-01 same-second tie-break (distinct candidates enforced)
- Stale write cannot supersede current head
- Workflow / channel kindless query exclusion
- Tenant confinement (alien channel rejected)
- FTS exclusion (NULL search_tsv confirmed)
- NIP-11 advertises NIP-37, not NIP-40

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-14 00:19:32 -04:00
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7andWill Pfleger 79a9a89a4d feat(relay): add NIP-37 draft wrap support (kind:31234)
Add kind:31234 as an author-only, channel-less, parameterized-replaceable
event kind for encrypted draft wraps per NIP-37.

Privacy enforcement spans every relay read path:
- WS REQ: AUTHOR_ONLY_KINDS gate closes the subscription with
  restricted: for any requester who isn't the author
- WS COUNT: same gate applied before the count query executes
- HTTP bridge /query + /count: post-filter and guard use AUTHOR_ONLY_KINDS
- Live fan-out: AUTHOR_ONLY_KINDS check in dispatch_persistent_event_inner
  prevents draft events from being pushed to non-author subscribers
- FTS (NIP-50): migration 0007 sets search_tsv = NULL for kind:31234,
  making drafts storage-level unsearchable

Ingest validation (validate_draft_wrap_envelope):
- Exactly one non-empty d tag (any bounded value; relay is grammar-agnostic)
- Exactly one k tag with canonical u16 decimal (no leading zeros, fits u16)
- No h or p outer tags (compose context belongs in encrypted payload only)
- Content: empty string (tombstone) or NIP-44 v2 ciphertext shape check
- Optional expiration: at most one, decimal, strictly future, ≤ safe integer

NIP-11 now advertises NIP-37. NIP-40 is intentionally not advertised
because Buzz does not yet suppress expired rows on read.

Schema migration 0007 extends the search_tsv generated column exclusion
list with kind 31234.

New tests:
- 23 unit tests for validate_draft_wrap_envelope in ingest.rs covering
  every acceptance and rejection path
- Comprehensive E2E test suite in e2e_nip37_draft.rs covering write
  validation, NIP-01 replacement ordering, tombstone persistence,
  author-only REQ/COUNT/HTTP, kindless/mixed filter privacy, known-d
  privacy tripwires, live fan-out isolation, and NIP-11 advertisement

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-14 00:19:32 -04:00
Mat BalezandGitHub 458c7f9154 [codex] Add view activity label to agent popover (#1748)
Signed-off-by: Mat Balez <60949391+matbalez@users.noreply.github.com>
2026-07-13 22:58:56 -04:00
a653406309 ci(desktop): surface flaky E2E tests instead of retry-masking them (#1838)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 22:14:40 -04:00
7e62a25af0 fix(desktop): treat channel creator as member before 39002 provisioning (#1830)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 22:14:28 -04:00
f3599f2cd4 fix(mobile): mirror app bar title padding when actions are empty (#1832)
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Brain <21994759fc7a6fa6b965551d35cfd7897d262f2495467f2d78694ddcfa6a5c7e@sprout-oss.stage.blox.sqprod.co>
2026-07-13 17:21:34 -07:00
259a1724d3 docs(mobile): backfill mobile changelog (#1835)
Signed-off-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1ux8n2yfs8qfvgd75s7kyhar2mztac355v6vmrz4juc9l3msw4pgstums9e <e18f3511303812c437d487ac4bf46ad897dc46946699b18ab2e60bf8ee0ea851@sprout-oss.stage.blox.sqprod.co>
2026-07-13 17:03:09 -07:00
f1706e23f7 BOT-1247 Configure Android Play identity and signing (#1829)
Signed-off-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
2026-07-13 16:29:35 -07:00
ffa2de0fba chore(release): release Buzz Mobile version 0.4.2 (#1833)
Signed-off-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
mobile-v0.4.2
2026-07-13 16:20:29 -07:00
3112e59fd8 fix(mobile): add mentioned agents to channels (#1696)
Signed-off-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
2026-07-13 16:17:01 -07:00
fdd366bc18 chore(release): release Buzz Mobile version 0.3.33 (#1828)
Signed-off-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
mobile-v0.3.33
2026-07-13 15:16:09 -07:00
Will PflegerandGitHub 9c4ed39382 chore(release): release Buzz Desktop version 0.4.2 (#1798) v0.4.2 2026-07-13 18:04:15 -04:00
1b4703021d Bound NIP-RS retention and search indexing (#1771)
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
2026-07-13 17:58:20 -04:00
d75c2e913e fix(desktop): unify observer feed scroll onto useAnchoredScroll (#1825)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 17:50:09 -04:00
b63a2e4231 fix(desktop): sync sidebar update card copy with global installing state (#1827)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 17:38:32 -04:00
34dcd13d55 fix(desktop): parse codex ACP plan entries[] into checklist (#1824)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 16:59:20 -04:00
020ac7f405 fix(desktop): resolve Git Bash for Windows shell tool via PATH/git/registry fallback (#1821)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 16:53:02 -04:00
51ee1c473d feat(desktop): show read-only MCP server config (#1780)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 16:29:21 -04:00
deed64a14f fix(desktop): require user action before applying updates (#1820)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 16:13:20 -04:00
c06ddcf14b fix(desktop): preserve selected inbox rows through reflow (#1817)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 15:16:07 -04:00
a227bb45f6 Refresh invite and repository web pages (#1808)
Signed-off-by: Fizz <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
2026-07-13 19:20:34 +01:00
a6cfd65511 fix(snapshot): send Buzz shares as PNG avatar cards (#1811)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 13:58:24 -04:00
d41b4c3905 test(desktop): consolidate and stabilize scroll coverage (#1815)
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co>
2026-07-13 10:11:27 -07:00
e34e0974a1 fix(buzz-agent): support max effort for gpt-5.6 family (#1806)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 12:57:21 -04:00
bc14052d39 fix(mobile): align Android AGP/Gradle/Kotlin with Flutter 3.41.7 (unbreak Android build) (#1775)
Signed-off-by: Aaron Goldsmith <aargoldsmith@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 09:30:38 -07:00
Will PflegerandGitHub fac79215a4 fix(desktop): widen probe test timing margins for parallel pre-push (#1812) 2026-07-13 12:28:25 -04:00
68909629f9 fix(desktop): keep pairing tests after production items (#1807)
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co>
2026-07-13 12:23:18 -04:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
f4c013d040 chore(deps): update rust crate rand to v0.10.2 (#1791)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-13 08:37:02 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
f1f5002a97 chore(deps): update rust crate nostr to v0.44.4 (#1789)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-13 08:36:47 -07:00
9b47c8548f Add optional standalone pairing relay to Helm chart (#1799)
Signed-off-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub12gtutshhh76rx0jx697f32f9tffd4hhp3hx58fp4x6u4uemkm7sqf8f757 <5217c5c2f7bfb4333e46d17c98a9255a52dadee18dcd43a43536b95e6776dfa0@sprout-oss.stage.blox.sqprod.co>
chart-v0.1.3
2026-07-13 11:36:04 -04:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
d38a7ef775 chore(deps): update radix-ui-primitives monorepo (#1779)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-13 15:35:41 +00:00
dbe7fb8534 Fix mobile relay reconnect lifecycle (#1772)
Signed-off-by: Wes <wesbillman@users.noreply.github.com>
Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co>
2026-07-13 15:25:58 +00:00
0950d392b7 fix(relay): publish membership snapshot on provisioning (#1761)
Signed-off-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@sprout-oss.stage.blox.sqprod.co>
2026-07-13 08:22:44 -07:00
bea507d8aa chore(ci): lint desktop Tauri crate in CI (#1801)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 11:21:10 -04:00
e09e94ab88 fix(desktop): preserve live events and window order across channel refreshes (#1802)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 11:20:02 -04:00
109800fe5a fix(desktop): restore macOS navigation chrome alignment (#1797)
Signed-off-by: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 <8a675edd33677aa0389f6650d467b2041fb0df4ca820eacb009babb95e3715d4@sprout-oss.stage.blox.sqprod.co>
2026-07-13 08:15:59 -07:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
c36448ef3e chore(deps): update rust crate ignore to v0.4.28 (#1788)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-07-13 15:07:16 +00:00
b32f2c08f7 fix(desktop): omit invalid snapshot imeta thumbnails (#1800)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-13 11:03:13 -04:00