Proxy remote cross-signing keys rather than caching them¶
- Status: accepted
- Date: 2026-09-11
Technical Story: 0.12.10, "devices of users on other servers always show as unverified".
Context and Problem Statement¶
Clients need a remote user's master and self-signing keys to decide whether that
user's devices, and the user, are verified. Merovingian learns them two ways:
the /keys/query federation proxy asks the user's server
(/user/keys/query), and the user's server pushes m.signing_key_update EDUs
when they change. Before 0.12.10 the proxy discarded the cross-signing keys and
the EDU was dropped as an unknown type.
When fixing both, the EDU could either be stored, as a cache the proxy reads, or treated purely as a signal.
Decision Drivers¶
- The device-key half of the same flow is already proxied live and never cached,
and an inbound
m.device_list_updateonly marks the user changed. - A cached copy can be served stale or duplicated next to a freshly proxied one.
JSON objects in the
/keys/queryresponse must not repeat a user ID. - An EDU arrives unrequested. Storing its keys means trusting the push path as
much as the pull path, and
store.cross_signing_keyscurrently holds local users only. Other code assumes that.
Considered Options¶
- Store the keys from
m.signing_key_updateincross_signing_keysand serve remote users from the store. - Keep proxying; treat
m.signing_key_updateexactly likem.device_list_update— put the user in local users'device_lists.changedso clients re-query.
Decision Outcome¶
Chosen option: keep proxying, and treat the EDU as a change signal only.
The shared inbound handler in local_http_router.cpp handles both EDU types.
handle_key_query collects cross-signing keys from the store for local users
only, and takes remote users' keys from their server's response after
federation::accept_remote_key_query_response() has filtered it.
The rule this sets for future code: cross_signing_keys holds local users'
keys only. If remote caching is ever added, it needs its own storage and must
replace — not add to — the proxied entry for that user in the response.
Positive Consequences¶
- One source of truth for remote keys: the remote server, at query time.
- No cache invalidation, no duplicate-key hazard in the response.
Negative Consequences¶
- Every
/keys/queryfor a remote user costs a federation round trip, as it already did for device keys. An unreachable server shows up infailuresinstead of being served from cache.
Links¶
- Related: ADR-0060