Callers never see stores: session_ctx/verify/user-info resolve the store
from the request host via satellite.store_for_host; session refresh and
logout eviction are dispatch functions too (satellite.refresh_session /
evict_session). API handlers keep one code path plus forward_request
one-liners; proxy.py folds into satellite.py; Domain.store and the
store parameters are gone; 503 comes from the dispatch point as a plain
HTTPException.
The sync protocol drops replay/generation/seq: snapshots are small, so
every connect starts from a full snapshot and a single ordered WebSocket
cannot gap; a slow subscriber is dropped and resyncs. The satellite
reconnects every refresh_interval to reconcile drift.
- _remote_headers and /check used struct convenience properties that
read the global database; they now use the SessionContext / the handed
store (also fixes Remote-Credential carrying a struct repr instead of
the UUID).
- Replica availability: TTL clock starts at disconnect, not at last
message or failed reconnect; tight WS keepalive for prompt dead-peer
detection.
- Proxy preserves repeated Set-Cookie via raw headers; sync endpoint
does its own accept (wsutil decorator pre-accepts) and bypasses host
dispatch (server-to-server; satellite may use an out-of-domain
address).
- Admin-credential bootstrap warning skips remote domains.
Verified live with two instances (remote :4501, satellite :4402):
replica snapshot + events, 204 forward with Remote-* in <1ms, validate
write-behind landing on the remote, proxied logout with instant local
eviction, 503 after cache_ttl of disconnect, resync after remote
restart.
ApiDomain carries the remote block (sync token write-only, never
echoed); create/patch accept it, validated with the combined config
(auth host mandatory for remote domains). db.update_domain replaces
remote wholesale like the other domain fields.