Skip to content

Boot assembly: build the realm list from trusted servers via _realm-auth#5285

Merged
lukemelia merged 6 commits into
cs-11655-add-realm-servers-event-typefrom
cs-11658-boot-assembly-trusted-servers
Jun 23, 2026
Merged

Boot assembly: build the realm list from trusted servers via _realm-auth#5285
lukemelia merged 6 commits into
cs-11655-add-realm-servers-event-typefrom
cs-11658-boot-assembly-trusted-servers

Conversation

@lukemelia

@lukemelia lukemelia commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

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.tsfetchUserRealmsFromTrustedServers(serverURLs) iterates the trusted-server URLs, POSTs _realm-auth on each, and returns the union of realm URLs. assertOwnRealmServer() preserves the single-server invariant; multi-realm-server federation is out of scope.
  • matrix-service.ts start() — reads app.boxel.realm-servers in parallel with favorites, hands the result to fetchUserRealmsFromTrustedServers, and pipes the URLs into setAvailableRealmIdentifiers and initSlidingSync. fetchCatalogRealms() is unchanged.
  • Runtime listener — an app.boxel.realm-servers account-data listener re-fetches via _realm-auth when the key changes at runtime. A trustedRealmServersAuthoritative flag makes the legacy app.boxel.realms listener skip setAvailableRealmIdentifiers while trusted servers are authoritative, so the initial-sync re-emission of the legacy event can't clobber the boot result. loginToRealms / loadMoreAuthRooms still run so post-login realm authentication isn't dropped.
  • Transition fallback — when app.boxel.realm-servers is empty or absent, boot falls back to reading the legacy app.boxel.realms key so users whose accounts haven't yet been migrated still boot. Removed once the lazy migration that populates app.boxel.realm-servers has run on all active accounts.

Behavior

  • Boot assembles the realm list from trusted servers via _realm-auth when app.boxel.realm-servers is populated.
  • Catalog realms still appear (fetchCatalogRealms() is untouched).
  • The single-server assertion (assertOwnRealmServer()) is preserved.

Scope

Test plan

  • Typecheck, lint, and prettier clean on the touched host files.
  • CI: integration tests in matrix-service-boot-assembly-test.ts — boot from trusted servers; fetchUserRealmsFromTrustedServers round-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

ylm and others added 2 commits June 18, 2026 17:14
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>
@lukemelia

Copy link
Copy Markdown
Contributor Author

[Codex] Finding: app.boxel.realms account-data events can still overwrite the new trusted-server boot result.

start() now derives userRealmURLs from app.boxel.realm-servers and _realm-auth (packages/host/app/services/matrix-service.ts:856-884), but the existing account-data listener still handles APP_BOXEL_REALMS_EVENT_TYPE by calling setAvailableRealmIdentifiers((e.event.content.realms as string[]).map(ri)) (same file:397-400). Since the listener is bound before startClient(), the initial Matrix sync can emit the existing legacy app.boxel.realms event after the new boot path has populated available realms. If that legacy event is stale or differs from _realm-auth, it will remove realms discovered from trusted servers or re-add stale legacy realms.

The new test does not catch this because its mock legacy activeRealms matches the _realm-auth result. Please either ignore/migrate legacy app.boxel.realms events when app.boxel.realm-servers is present, add handling for APP_BOXEL_REALM_SERVERS_EVENT_TYPE that recomputes via _realm-auth, or add a regression test where legacy realms differ from _realm-auth and verify the trusted-server result survives startClient().

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>
@lukemelia

Copy link
Copy Markdown
Contributor Author

Good catch — fixed in 4bb2b12. The legacy app.boxel.realms listener was indeed clobbering the trusted-servers boot result via startClient()'s synthetic AccountData re-emit.

Changes:

  • New trustedRealmServersAuthoritative flag set during boot (true when app.boxel.realm-servers has entries) and at runtime by the new realm-servers listener.
  • Legacy app.boxel.realms listener now skips setAvailableRealmIdentifiers while the flag is true. loginToRealms / loadMoreAuthRooms still run so authentication for new realms isn't dropped.
  • New APP_BOXEL_REALM_SERVERS_EVENT_TYPE listener re-fetches via _realm-auth and updates the available-realms list — runtime counterpart to the new boot path.
  • Regression test where mock activeRealms = [] but realmPermissions advertises two realms via _realm-auth; both must survive startClient(). Without the gating it fails.

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Preview deployments

Host Test Results

    1 files  ±0      1 suites  ±0   1h 56m 14s ⏱️ - 2m 45s
3 153 tests ±0  3 138 ✅ ±0  15 💤 ±0  0 ❌ ±0 
3 172 runs  ±0  3 157 ✅ ±0  15 💤 ±0  0 ❌ ±0 

Results for commit e3265b0. ± Comparison against earlier commit 27d88c7.

Realm Server Test Results

    1 files  ±0      1 suites  ±0   13m 11s ⏱️ +39s
1 745 tests ±0  1 745 ✅ ±0  0 💤 ±0  0 ❌ ±0 
1 838 runs  ±0  1 838 ✅ ±0  0 💤 ±0  0 ❌ ±0 

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>
@lukemelia lukemelia marked this pull request as ready for review June 22, 2026 19:16
@lukemelia lukemelia requested review from a team and habdelra June 22, 2026 19:17
@habdelra habdelra requested a review from Copilot June 22, 2026 19:21

Copilot AI 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.

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-auth and union the returned realm URLs (while preserving the single-realm-server invariant).
  • Update MatrixService.start() and account-data event handling to prefer app.boxel.realm-servers for realm assembly and prevent legacy app.boxel.realms events 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>
Comment thread packages/host/app/services/matrix-service.ts Outdated
@lukemelia lukemelia changed the title CS-11658: boot assembly from trusted servers via _realm-auth Boot assembly: build the realm list from trusted servers via _realm-auth Jun 23, 2026
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>
@lukemelia lukemelia merged commit 59df40e into cs-11655-add-realm-servers-event-type Jun 23, 2026
72 of 73 checks passed
lukemelia pushed a commit that referenced this pull request Jun 23, 2026
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>
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.

3 participants