Skip to content

crypto: cache verified member identities per group session + batch identity lookups - #88

Merged
smallacc551collabagents merged 3 commits into
mainfrom
perf/group-identity-cache
Oct 30, 2026
Merged

smallacc551collabagents merged 3 commits into
mainfrom
perf/group-identity-cache

Conversation

@smallacc551collabagents

Copy link
Copy Markdown
Owner

Group sends re-fetched every member's identity on every send (/keys/identity per member for the device re-query, plus the revocation check), and safety() ran a per-member verifier.refresh loop.

What changed (crypto + minimal frontend glue)

  • groups.ts: per-session cache of the GroupInfo and of the membership version whose member devices were verified. send() reuses the cached group and calls shareGroupKey({ requery: false }) unless one of these happens: the membership version changed, a 409 membership_changed (cache dropped, refetch + rotate), a member has a pendingChange (re-checked every send until accepted), or a to-device message arrives from a device other than the verified one. There's also an explicit markIdentitiesChanged(). refreshMembers(id, { force }) defaults to force.
  • backend-bridge.ts: fetchIdentities() makes one POST /keys/identity/batch {usernames} (Backend). On 404 it falls back to deduped Promise.all GETs and remembers that per client, so the client works before and after the deploy. keys.query uses it.
  • revocation.ts: prime(userIds) batch-refreshes stale entries. assertTrusted still compares against the exact device and records pendingChange (fails closed).
  • olm-adapter.ts: honours requery:false and calls gate.prime once per share.
  • frontend live.ts: the per-member verifier.refresh loop in safety() is gone. It now calls refreshMembers(id, { force }) with the existing 60s throttle.

Security: a replaced device still goes to pendingChange and gets no room keys until acceptMemberIdentity (test: identity-cache.test.ts, "identity gate keeps its security").

Call counts (fake backend, group of 4, 5 sends, revocation maxAge 0 so every send re-confirms):

identity calls / 5 sends
before (main) 30 GET /keys/identity
after, no batch route (404) 15 GET (1 per member per send, deduped)
after, batch route live 5 POST batch, 0 GET
In production the revocation cache is 5 min, so a send within that window makes 0 identity calls.

Tests: 91/91 pass (4 new). tsc and eslint are clean.

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.

1 participant