CS-11655: add app.boxel.realm-servers event type + read/write helpers#5284
CS-11655: add app.boxel.realm-servers event type + read/write helpers#5284lukemelia wants to merge 7 commits into
Conversation
Preview deploymentsHost Test Results 1 files ±0 1 suites ±0 1h 57m 14s ⏱️ - 2m 38s Results for commit 28e0b6a. ± Comparison against earlier commit 59df40e. Realm Server Test Results 1 files ±0 1 suites ±0 10m 46s ⏱️ -18s Results for commit 28e0b6a. ± Comparison against earlier commit 59df40e. |
lukemelia
left a comment
There was a problem hiding this comment.
[Codex] I reviewed PR 5284 and found one issue.
| ); | ||
| let realms = Array.isArray(existing.realms) ? existing.realms : []; | ||
| if (realms.includes(realmURL)) { | ||
| let entries = Array.isArray(existing[arrayKey]) |
There was a problem hiding this comment.
[Codex] This changes the legacy app.boxel.realms append behavior too: appendRealmToUserAccountData now routes through this helper, so any existing non-string entries in the realms array are filtered out and the PUT below writes back only entries. The previous implementation preserved the existing array contents exactly with realms: [...realms, realmURL]. Since this PR calls out legacy app.boxel.realms behavior as unchanged, please avoid sanitizing the stored array on write; if we want a string-only view for includes, keep that separate from the array we persist back.
There was a problem hiding this comment.
low-risk, leaving as is
There was a problem hiding this comment.
Pull request overview
Adds plumbing for a new Matrix account-data event type (app.boxel.realm-servers) to store a user’s trusted realm-server origins, alongside helper APIs and test coverage across runtime-common, host, and realm-server.
Changes:
- Introduces
APP_BOXEL_REALM_SERVERS_EVENT_TYPEandAppBoxelRealmServersContentinruntime-common. - Adds host
matrix-serviceread/write/append/remove helpers for the new account-data key, plus mock-matrix support and a new integration test. - Extends realm-server Synapse helpers and the Grafana upsert permission flow to keep
app.boxel.realmsandapp.boxel.realm-serversin sync, with new endpoint tests.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/runtime-common/matrix-constants.ts | Adds new Matrix account-data event type constant and payload interface. |
| packages/realm-server/tests/server-endpoints/maintenance-endpoints-test.ts | Adds tests for realm-server account-data append behavior and Grafana upsert syncing. |
| packages/realm-server/synapse.ts | Refactors account-data append plumbing to support both realms and realm-servers. |
| packages/realm-server/handlers/handle-upsert-realm-user-permission.ts | Updates upsert flow to also append realm-server origin to new account-data key. |
| packages/matrix/support/matrix-constants.ts | Adds the new event type constant in matrix support utilities. |
| packages/host/tests/integration/matrix-service-realm-servers-test.ts | Adds integration tests for host matrix-service realm-servers account-data helpers. |
| packages/host/tests/helpers/mock-matrix/_client.ts | Extends mock Matrix client to get/set/route the new account-data event type. |
| packages/host/tests/helpers/mock-matrix.ts | Adds activeRealmServers option for mock-matrix configuration. |
| packages/host/app/services/matrix-service.ts | Adds host helpers to read/write/append/remove realm-server URLs in account data. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public async getRealmServersFromAccountData(): Promise<string[]> { | ||
| let { realmServers = [] } = | ||
| ((await this.client.getAccountDataFromServer( | ||
| APP_BOXEL_REALM_SERVERS_EVENT_TYPE, | ||
| )) as { realmServers: string[] }) ?? {}; | ||
| return realmServers; | ||
| } |
There was a problem hiding this comment.
[Claude Code 🤖] Leaving as-is. app.boxel.realm-servers is a brand-new event type with no legacy/migrated data, and the only writers (these host helpers plus the server-side append) always persist a string[]. The absent case is already handled by ?? {} → []. These helpers also intentionally mirror the existing legacy realms helpers, which use the same un-normalized cast. Same low-risk call as the parallel Codex thread above.
Foundational data-model change for the source-of-truth rework. A user's
matrix account data should store the set of *trusted realm servers*
alongside the existing flat realm list during the transition. Boot
assembly and lazy migration land in follow-ups (CS-11658, CS-11659).
- runtime-common: new APP_BOXEL_REALM_SERVERS_EVENT_TYPE constant and
AppBoxelRealmServersContent payload type.
- host matrix-service: get/set/append/remove helpers for the new key,
mirroring the existing realms helpers. Legacy realms behavior is
unchanged.
- realm-server synapse: parallel appendRealmServerToUserAccountData
with the same idempotent retry-on-stomp semantics (helpers factored
to share the get/put plumbing).
- realm-server upsert-permission handler: after the realm append,
also writes the realm-server origin to app.boxel.realm-servers so
both keys stay in lockstep during the transition.
- mock matrix client: handles get/set + AccountData event routing for
the new key, with a new activeRealmServers config option.
- tests: host integration test round-trips the {realmServers} payload
through the helpers (append idempotency, remove); realm-server
endpoint tests cover direct helper behaviour and the grafana
upsert sync path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
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 to
28e0b6a
Compare
Summary
Foundational data-model change for the Rework Realms List Source-of-Truth project. Introduces the
app.boxel.realm-serversaccount-data event type so a user's Matrix account data can carry the set of trusted realm servers alongside the existing flat realm list. Boot assembly (CS-11658) and lazy migration (CS-11659) land in follow-ups; this PR is plumbing-only.APP_BOXEL_REALM_SERVERS_EVENT_TYPEconstant andAppBoxelRealmServersContentpayload type.get/set/append/remove RealmServers...AccountDatahelpers mirroring the existing realms helpers. Legacyapp.boxel.realmsbehavior is unchanged.appendRealmServerToUserAccountDatawith the same idempotent retry-on-stomp semantics. The existing fetch/put plumbing is factored to take aneventTypeso both helpers share it.app.boxel.realm-serversso both keys stay in lockstep going forward.activeRealmServersconfig option.Acceptance criteria (from CS-11655)
runtime-common.app.boxel.realm-serversaccount data exist and are typed.app.boxel.realmsbehavior is unchanged.Test plan
pnpm lint:typesclean inruntime-common,host,realm-server.pnpm lint:jsclean inruntime-common,host,realm-server.pnpm prettier --checkon all touched files.Integration | matrix-service | realm-servers account data— round-trips the payload, append idempotency, remove leaves other entries.appendRealmServerToUserAccountDatapreserves prior entries; grafana upsert populatesapp.boxel.realm-serverswith the realm-server origin.🤖 Generated with Claude Code