Files
buzz/mobile/lib/shared/identity_archives
Tom Brow 5e52fa9831 perf(mobile): download the identity archive list only when it changed (#9445)
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.

---------

Signed-off-by: Tom Brow <tomb@squareup.com>
Co-authored-by: Tom Brow <tomb@squareup.com>
2026-11-13 14:40:06 -08:00
..