Skip to content

perf(mobile): download the identity archive list only when it changed - #9445

Merged
brow merged 4 commits into
mainfrom
tomb/mobile-identity-archive-since
Nov 13, 2026
Merged

brow merged 4 commits into
mainfrom
tomb/mobile-identity-archive-since

Conversation

@brow

@brow brow commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

Every reconnect refetches the community's identity archive list (kind 13535) so the agent pickers stay current. On Block's Builderlab that list is about 228 KB, and it is downloaded again on every resume even though it rarely changes.

The provider now remembers the last verified list per community and relay authority, and asks the relay only for a newer one (since the known list's created_at plus one). When nothing newer exists it reuses the known list, as the same set instance so watchers (mention candidates, search, the Agents page, channel details, the members sheet) don't rebuild. A newer list replaces it after the same verification as before. A list that fails any check, including its signature, yields no archived identities and drops the remembered one.

Validation

Provider tests: a refresh with an unchanged list sends since, keeps the known set and leaves the hidden set's listeners untouched; a newer list replaces it and becomes the new baseline; every invalid list (wrong signer, missing - marker, bad signature) starting from a warm cache drops the remembered list, so a following empty answer hides nobody. Each new step fails with its fix reverted. The existing verification, community-switch and deadline tests pass with the relay fake now honoring since. Full mobile test suite (4803 passed) and flutter analyze pass.

On an iPhone 12 mini signed in to Block's Builderlab (instrumented profile build), a resume after 60 s in the background downloaded the 228 KB list each time before this change.

Regressions

Accepted tradeoff: a same-second list can be missed. The cursor assumes each list the relay publishes is stamped strictly later than the previous one. The relay nearly always does this (publish_nipia_archival_list stamps past the current head and republishes on drift), but two lists published within one second can share a created_at, and NIP-IA then keeps the one with the lower id. If the app already holds the other one, it misses the replacement until a later list arrives (the next archive or unarchive) or the app restarts. Meanwhile a just-archived or just-unarchived agent can show wrongly in pickers and search, never in history. We accept this for the bandwidth and resume-time win; a relay follow-up that stamps each list under the replacement lock and makes the rule a NIP-IA MUST removes the case.

The same bound applies if a relay's database is restored to an older list: the app keeps its newer one until the relay publishes a list newer than it, or the app restarts.

A failed or timed-out fetch still hides nobody, as before.

@brow

brow commented Nov 13, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-13T19:10:16.117846Z 1f3822e Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: f0e02a0637

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@brow
brow marked this pull request as ready for review November 13, 2026 19:08
@brow
brow requested a review from a team as a code owner November 13, 2026 19:08
@brow
brow deployed to codex-review November 13, 2026 19:08 — with GitHub Actions Active
@github-actions github-actions Bot added the codex-security-review-current The posted Codex security review matches its recorded range. label Nov 13, 2026
@github-actions

github-actions Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated, security-focused review generated by Codex.
Use it as a supplement to human review; false positives are possible.

Scope

  • Exact PR diff: 59ae028a360242d43ffbfdafca8a76eda00484d9...104c915199ce33f295761817b7618d0ee05bc2c8
  • Model: gpt-6.1-sol

💡 Click "edited" above to see earlier reviews for this PR.


Review Summary

Overall Risk: NONE

No concrete security, correctness, or reliability findings in the authorized PR range after tracing cache scoping, event verification, relay replacement semantics, and callers.

Findings

No concrete security, correctness, or reliability findings were identified.

Notes

  • Review used read-only inspection. Tests and builds were not executed.

Generated by Codex Security Review |
Requested by: @brow |
Workflow run

Each reconnect refetched the relay's 13535 archive list (about 228 KB on
Builderlab). Remember the last verified list per community and authority
and ask only for a newer one.

Signed-off-by: Tom Brow <[email protected]>
@brow
brow force-pushed the tomb/mobile-identity-archive-since branch from 1f3822e to 104c915 Compare November 13, 2026 19:36
@github-actions github-actions Bot removed the codex-security-review-current The posted Codex security review matches its recorded range. label Nov 13, 2026
@brow
brow deployed to codex-review November 13, 2026 19:36 — with GitHub Actions Active
@github-actions github-actions Bot added the codex-security-review-current The posted Codex security review matches its recorded range. label Nov 13, 2026

@kazemek21 kazemek21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Kyle reviewed this PR and the Nexus review-tracker notes and approved with no comments.

Posted by Codex (gpt-6.1-sol) on behalf of kylez.

@brow
brow merged commit 5e52fa9 into main Nov 13, 2026
53 checks passed
@brow
brow deleted the tomb/mobile-identity-archive-since branch November 13, 2026 22:40
wpfleger96 added a commit that referenced this pull request Nov 14, 2026
* origin/main: (24 commits)
  docs(readme): point desktop readers at block/buzz-app and describe where each client lives (#9493)
  test(cli,db): freeze the assertion test clock and give pool release tests a test-lane acquire budget (#9442)
  test(desktop): unmount every TerminalSubstrate tree before the window closes (#9440)
  fix(mobile): drop the iOS share extension from the build until it's signed (#9489)
  chore(release): release Buzz Desktop version 0.5.30 (#9491)
  perf(mobile): parse each event's thread reference once (#9460)
  perf(mobile): load only the directory when search opens (#9466)
  perf(mobile): stop rebuilding Home's channel list when a route covers it (#9459)
  perf(mobile): ask typing subscriptions only for the typing window (#9454)
  perf(mobile): skip avatar URL parsing for non-HTTP URLs (#9448)
  perf(mobile): download the identity archive list only when it changed (#9445)
  perf(mobile): don't treat a Control Center pull as a resume (#9443)
  fix(mobile): keep a superseded roster read out of the offline snapshot (#9452)
  perf(mobile): read channel bot roles from the member list (#9451)
  perf(mobile): read Activity once per reconnect (#9455)
  fix(mobile): offer role controls for agents (#9475)
  perf(mobile): stop rebuilding the native nav bar on every scrolled frame (#9458)
  perf(mobile): keep verified-name batches open as people are added (#9450)
  perf(mobile): read only changed custom emoji sets on reconnect (#9446)
  perf(mobile): keep latest-message times on a directory load (#9457)
  ...

Signed-off-by: Will Pfleger <[email protected]>

# Conflicts:
#	crates/buzz-cli/src/desktop_provider.rs
bostonaholic added a commit that referenced this pull request Nov 14, 2026
…eaction-recipients

* origin/main: (111 commits)
  perf(acp): avoid repeated observer batch serialization (#9483)
  docs(readme): point desktop readers at block/buzz-app and describe where each client lives (#9493)
  test(cli,db): freeze the assertion test clock and give pool release tests a test-lane acquire budget (#9442)
  test(desktop): unmount every TerminalSubstrate tree before the window closes (#9440)
  fix(mobile): drop the iOS share extension from the build until it's signed (#9489)
  chore(release): release Buzz Desktop version 0.5.30 (#9491)
  perf(mobile): parse each event's thread reference once (#9460)
  perf(mobile): load only the directory when search opens (#9466)
  perf(mobile): stop rebuilding Home's channel list when a route covers it (#9459)
  perf(mobile): ask typing subscriptions only for the typing window (#9454)
  perf(mobile): skip avatar URL parsing for non-HTTP URLs (#9448)
  perf(mobile): download the identity archive list only when it changed (#9445)
  perf(mobile): don't treat a Control Center pull as a resume (#9443)
  fix(mobile): keep a superseded roster read out of the offline snapshot (#9452)
  perf(mobile): read channel bot roles from the member list (#9451)
  perf(mobile): read Activity once per reconnect (#9455)
  fix(mobile): offer role controls for agents (#9475)
  perf(mobile): stop rebuilding the native nav bar on every scrolled frame (#9458)
  perf(mobile): keep verified-name batches open as people are added (#9450)
  perf(mobile): read only changed custom emoji sets on reconnect (#9446)
  ...

Signed-off-by: Matthew Boston <[email protected]>
brow added a commit that referenced this pull request Nov 14, 2026
…s one (#9476)

### Why

When an admin archives or unarchives an identity, the relay publishes a
new archived identities list (kind 13535). This list replaces the
previous one. It contains all the identities that are archived in the
community. Clients use it to hide archived agents from pickers and
search.

The list can be large (about 228 KB on Block's Builderlab), and it
seldom changes. Thus the mobile app wants to download it only when it
changed. To do this, the app keeps the last list it received, and asks
the relay only for a list that is newer than that one (`since` its
`created_at` plus one, in [#9445 archive list
cache](#9445)).

This works only if each new list has a `created_at` that is later than
the previous list's. Today the relay does not always do this:

- The relay calculates the new list's `created_at` as "the current
list's `created_at` plus one second". But it reads the current list
before it gets the lock that controls replacement.
- Thus, when two archive requests occur at the same time, two publishers
can read the same current list. Then both sign a list with the same
`created_at`. The store keeps the one with the lower event id.
- A client that already has the other list does not see the replacement,
because it asks only for lists with a later `created_at`. It continues
to hide or show the wrong agents until the next archive change or an app
restart.
- With many concurrent publishers it is worse: a publisher can lose that
tie again and again, and then stop with `failed to publish kind:13535
after 8 concurrent replacements`. Then no list contains the latest
archive change.

### What changes

- New `Db::replace_addressable_event_after_head`. It gets the
replacement lock first. Then it reads the current list. Then it signs
and stores the new list. Thus only one publisher at a time calculates
the next `created_at`, and each list is stamped strictly later than the
previous one.
- `publish_nipia_archival_list` uses this method. The check that
publishes again when the archive state changed during a publish does not
change.
- NIP-IA now makes this a rule: a relay MUST publish each `kind:13535`
with a `created_at` strictly greater than its previous one. Clients MAY
then ask only for lists `since` their list's `created_at` plus one.

### Validation

New relay test: 16 concurrent publishers all complete, and each stored
list (superseded rows included) has a strictly later `created_at`. On
main the same test fails, because a publisher uses all its retries. The
test needs Postgres and Redis, so it is in `postgres_tests` and runs in
the PostgreSQL Tests lane; a missing database or Redis makes it fail,
not pass. The existing archive tests pass against a local Postgres,
including the stale-publisher test. `just test` passes all 160 steps
except one NIP-FI session deadline test
(`deadline_refused_permit_still_queues_one_denial_when_cancel_wins`),
which this change does not touch and which passes 5 of 5 reruns alone.
Clippy passes.

### Regressions

None expected.

- A publisher now holds the replacement lock a little longer: for one
read and one signature. Only archive-list writes for the same community
wait on this lock, and archive changes are rare.
- The new `Db` method is an addition. `replace_addressable_event` and
its callers do not change.
- Until this is deployed, relays keep the old behavior. [#9445 archive
list cache](#9445) accepts that
same-second case on its own.

**Accepted limitation: a database restore.** The rule holds only while
the relay keeps its newest list. If an operator restores the relay from
a backup, the relay calculates the next `created_at` from the restored
list (`max(restored + 1, now)`). If earlier fast publishes went ahead of
the clock, that value can be at or below a `created_at` that a client
already has. That client then stays stale until a list passes its cached
`created_at`, or the app restarts. This change adds no repair for a
restore.

---------

Signed-off-by: Tom Brow <[email protected]>
Co-authored-by: Tom Brow <[email protected]>

This branch was successfully deployed

1 active deployment
codex-review — 104c9151 Deployed Nov 13, 2026 by brow via Run Codex Security Review #15023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex-security-review-current The posted Codex security review matches its recorded range.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants