Context
Drainpipe already receives #identity and #account events from the Jetstream firehose (the Rust Jetstream library deserializes them), but currently drops them on the floor — only Commit events are forwarded to the app.
Meanwhile, the app resolves DID documents and handles live during server-side rendering for every post author, commenter, etc. This means page loads depend on external HTTP calls to plc.directory (for did:plc) or user domains (for did:web), cached with 1-hour revalidation. On cold cache or cache expiry, a slow or unreachable identity source can block the entire page render for up to 5 seconds.
Proposal
Decouple identity resolution from page rendering by caching handles in our own database, updated reactively via the firehose:
- Store resolved handles in the database alongside DIDs — rendering reads from the DB instead of doing live DID resolution
- Forward identity/account events from drainpipe to the app (new endpoint or extend
receive_hook)
- Update cached handles reactively when identity events arrive — re-resolve the DID document and update the stored handle
- Periodic background re-resolution for did:web only as a safety net, since identity events are best-effort (changes can happen without events firing). This only applies to did:web users — did:plc changes reliably emit events via plc.directory. The did:web user count is tiny (~50 in the whole ecosystem), so this is negligible work.
Page renders become purely local DB reads. Identity resolution becomes a background process driven by firehose events.
Implementation notes
- The Rust Jetstream library already has full type definitions for
IdentityEvent and AccountEvent — deserialization infrastructure is ready
- Jetstream sends identity/account events to all subscribers regardless of
wantedCollections filters, so drainpipe is already receiving them
- Identity events are best-effort signals — the spec recommends re-resolving the DID document independently to verify
#account events include a status field (deleted, suspended, deactivated, takendown) which could be used to mark accounts inactive
References
Context
Drainpipe already receives
#identityand#accountevents from the Jetstream firehose (the Rust Jetstream library deserializes them), but currently drops them on the floor — onlyCommitevents are forwarded to the app.Meanwhile, the app resolves DID documents and handles live during server-side rendering for every post author, commenter, etc. This means page loads depend on external HTTP calls to plc.directory (for did:plc) or user domains (for did:web), cached with 1-hour revalidation. On cold cache or cache expiry, a slow or unreachable identity source can block the entire page render for up to 5 seconds.
Proposal
Decouple identity resolution from page rendering by caching handles in our own database, updated reactively via the firehose:
receive_hook)Page renders become purely local DB reads. Identity resolution becomes a background process driven by firehose events.
Implementation notes
IdentityEventandAccountEvent— deserialization infrastructure is readywantedCollectionsfilters, so drainpipe is already receiving them#accountevents include astatusfield (deleted,suspended,deactivated,takendown) which could be used to mark accounts inactiveReferences