Boot assembly: build the realm list from trusted servers via _realm-auth#5285
Conversation
Replace the "account data is the realm list" boot path with "account data is the list of trusted servers; ask each server which realms the user has." Builds on CS-11655 which introduced the new `app.boxel.realm-servers` account-data event type. - realm-server: new fetchUserRealmsFromTrustedServers() iterates the trusted-server URLs, POSTs _realm-auth on each, and returns the union of realm URLs. Preserves the single-server invariant by calling assertOwnRealmServer() — multi-realm-server federation is out of scope for v1. - matrix-service start(): reads APP_BOXEL_REALM_SERVERS_EVENT_TYPE in parallel with favorites and assembles user realms via the new helper. Hands the result to setAvailableRealmIdentifiers and initSlidingSync (replacing the direct read of app.boxel.realms). fetchCatalogRealms() is unchanged. - Transition fallback: when app.boxel.realm-servers is absent or empty, the boot falls back to reading the legacy app.boxel.realms key so existing users aren't broken before CS-11659's lazy migration has run on their account. The fallback is clearly marked for removal once that migration ships. - Tests: boot populates the realm list from the trusted-servers path; direct unit coverage of the new method (round-trip, empty input short-circuit, non-own server rejection); fallback module verifies the legacy path still works when realm-servers is empty. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Issue per-server _realm-auth requests concurrently via Promise.all, then union the returned realm URLs. Failure semantics unchanged: the first rejection still surfaces (graceful degradation is CS-11667). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
[Codex] Finding:
The new test does not catch this because its mock legacy |
Addresses the Codex review on PR #5285. The matrix sync triggered by `startClient()` re-emits the existing `app.boxel.realms` AccountData event, and the listener bound during `bindEventListeners` was overwriting the trusted-servers boot result with the legacy key's content. - matrix-service: track `trustedRealmServersAuthoritative`. Boot sets it true when `app.boxel.realm-servers` has entries; the new realm-servers listener flips it on at runtime if the key gains content. - Legacy `app.boxel.realms` listener: skip `setAvailableRealmIdentifiers` while the flag is true. Login side effects (loginToRealms, loadMoreAuthRooms) still run so authentication for new realms isn't dropped. - New `app.boxel.realm-servers` listener: re-fetch via _realm-auth, call setAvailableRealmIdentifiers, then loginToRealms / loadMoreAuthRooms post-login. Natural runtime counterpart to the new boot path. - Regression test: a setup where mock activeRealms = [] but realmPermissions advertises two realms via _realm-auth verifies both _realm-auth realms survive `startClient`'s synthetic event. Without the listener gating the test fails. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Good catch — fixed in 4bb2b12. The legacy Changes:
|
Preview deploymentsHost Test Results 1 files ±0 1 suites ±0 1h 56m 14s ⏱️ - 2m 45s Results for commit e3265b0. ± Comparison against earlier commit 27d88c7. Realm Server Test Results 1 files ±0 1 suites ±0 13m 11s ⏱️ +39s Results for commit e3265b0. ± Comparison against earlier commit 27d88c7. |
The app.boxel.realm-servers AccountData handler is async, so when fetchUserRealmsFromTrustedServers rejects (e.g. a list that isn't the user's own realm server), the rejection escapes as an unhandled rejection and takes down the app. Catch it: the authoritative, fail-loud assembly is the start()-time path; the reactive handler logs and leaves the available-realms list intact. Also simplify the trusted-servers-survives-legacy-event test to a single served realm. The second realm was advertised only to the matrix client's getJWT, not the realm-server mock's _realm-auth, so it never came back from boot assembly; the single-realm case fully proves the empty legacy event doesn't clobber the trusted-servers result. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Updates host boot behavior to treat Matrix account data app.boxel.realm-servers as the source-of-truth for which realm servers a user trusts, and derives the user’s realm list by calling each trusted server’s /_realm-auth endpoint (with a legacy fallback to app.boxel.realms during migration).
Changes:
- Add
RealmServerService.fetchUserRealmsFromTrustedServers(serverURLs)to POST/_realm-authand union the returned realm URLs (while preserving the single-realm-server invariant). - Update
MatrixService.start()and account-data event handling to preferapp.boxel.realm-serversfor realm assembly and prevent legacyapp.boxel.realmsevents from clobbering the boot result when authoritative. - Add integration tests intended to cover trusted-servers boot assembly + legacy fallback behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| packages/host/app/services/realm-server.ts | Adds trusted-server realm discovery via /_realm-auth with single-server assertion preserved. |
| packages/host/app/services/matrix-service.ts | Switches boot assembly to trusted servers, adds authoritative-flag gating for legacy realms events, and keeps legacy fallback during migration. |
| packages/host/tests/integration/matrix-service-boot-assembly-test.ts | Adds integration tests for trusted-server boot assembly and legacy fallback (but see PR comments about masking). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The three boot-assembly assertions were masked by setupIntegrationTestRealm(), which backfills the integration realm URL into availableRealmIdentifiers, so includes(ri(testRealmURL)) passed even if the boot path produced the wrong list. Stop autostarting Matrix during realm setup, clear the backfilled identifier, then run start() explicitly so the boot path alone is responsible for the populated list. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Strip ticket IDs, version labels, and temporal phrasing from the comments introduced for trusted-server boot assembly so they describe the current contract rather than the delivery state. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
59df40e
into
cs-11655-add-realm-servers-event-type
Addresses the Codex review on PR #5285. The matrix sync triggered by `startClient()` re-emits the existing `app.boxel.realms` AccountData event, and the listener bound during `bindEventListeners` was overwriting the trusted-servers boot result with the legacy key's content. - matrix-service: track `trustedRealmServersAuthoritative`. Boot sets it true when `app.boxel.realm-servers` has entries; the new realm-servers listener flips it on at runtime if the key gains content. - Legacy `app.boxel.realms` listener: skip `setAvailableRealmIdentifiers` while the flag is true. Login side effects (loginToRealms, loadMoreAuthRooms) still run so authentication for new realms isn't dropped. - New `app.boxel.realm-servers` listener: re-fetch via _realm-auth, call setAvailableRealmIdentifiers, then loginToRealms / loadMoreAuthRooms post-login. Natural runtime counterpart to the new boot path. - Regression test: a setup where mock activeRealms = [] but realmPermissions advertises two realms via _realm-auth verifies both _realm-auth realms survive `startClient`'s synthetic event. Without the listener gating the test fails. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Boot assembles the user's realm list from their trusted realm-servers rather than reading it directly out of account data. Account data (
app.boxel.realm-servers) lists the trusted servers; the host asks each server which realms the user has.realm-server.ts—fetchUserRealmsFromTrustedServers(serverURLs)iterates the trusted-server URLs, POSTs_realm-authon each, and returns the union of realm URLs.assertOwnRealmServer()preserves the single-server invariant; multi-realm-server federation is out of scope.matrix-service.tsstart()— readsapp.boxel.realm-serversin parallel with favorites, hands the result tofetchUserRealmsFromTrustedServers, and pipes the URLs intosetAvailableRealmIdentifiersandinitSlidingSync.fetchCatalogRealms()is unchanged.app.boxel.realm-serversaccount-data listener re-fetches via_realm-authwhen the key changes at runtime. AtrustedRealmServersAuthoritativeflag makes the legacyapp.boxel.realmslistener skipsetAvailableRealmIdentifierswhile trusted servers are authoritative, so the initial-sync re-emission of the legacy event can't clobber the boot result.loginToRealms/loadMoreAuthRoomsstill run so post-login realm authentication isn't dropped.app.boxel.realm-serversis empty or absent, boot falls back to reading the legacyapp.boxel.realmskey so users whose accounts haven't yet been migrated still boot. Removed once the lazy migration that populatesapp.boxel.realm-servershas run on all active accounts.Behavior
_realm-authwhenapp.boxel.realm-serversis populated.fetchCatalogRealms()is untouched).assertOwnRealmServer()) is preserved.Scope
mainautomatically.app.boxel.realm-serversfrom the legacy key for existing users.Test plan
matrix-service-boot-assembly-test.ts— boot from trusted servers;fetchUserRealmsFromTrustedServersround-trip, empty input, and non-own-server rejection; the legacy event not clobbering the trusted-servers result; and the legacy fallback.🤖 Generated with Claude Code