98e40dac97
CLI Smoke Test / smoke-test-linux (20) (push) Waiting to run
CLI Smoke Test / smoke-test-linux (24) (push) Waiting to run
CLI Smoke Test / smoke-test-windows (20) (push) Waiting to run
CLI Smoke Test / smoke-test-windows (24) (push) Waiting to run
Expo App TypeScript typecheck / typecheck (push) Waiting to run
3.2 KiB
3.2 KiB
Realtime Sync and RPC
This is the high-level doc for how Happy uses Socket.IO for realtime sync and point-to-point RPC.
Related docs:
protocol.md: wire contract, event names, and payload shapesmulti-process.md: deeper notes about cross-replica behavior, failure modes, and test historybackend-architecture.md: server subsystem overviewcli-architecture.md: daemon and client-side socket ownership
Core Pieces
Happy uses one Socket.IO endpoint at /v1/updates and three connection scopes:
user-scoped: app/web clients and account-wide listenerssession-scoped: one live session processmachine-scoped: one daemon for one machine
On the server:
socket.tsauthenticates the handshake, tags the socket withuserIdand scope metadata, and enables the Redis streams adapter whenREDIS_URLis set.eventRouter.tshandles fan-out for normal realtime updates.rpcHandler.tshandlesrpc-register,rpc-unregister, andrpc-call.
On the client side:
ApiSessionClientowns a long-lived session-scoped socket.ApiMachineClientowns a long-lived machine-scoped socket.- the app's
apiSocketowns a long-lived user-scoped socket. RpcHandlerManagerregisters handlers and re-registers them on reconnect.
Room Model
Normal fan-out rooms:
user:<userId>user:<userId>:user-scopeduser:<userId>:session:<sessionId>user:<userId>:machine:<machineId>
RPC registration rooms:
rpc:<userId>:<prefixedMethod>
The server uses room membership as the source of truth for who currently owns an RPC method.
Realtime Sync Flow
- A client connects with a scope (
user-scoped,session-scoped, ormachine-scoped). - The server adds that socket to the appropriate user/session/machine rooms.
- When durable state changes,
eventRouteremitsupdateevents to the matching rooms. - When transient presence changes, the server emits
ephemeralevents to the matching rooms. - On reconnect, clients can re-fetch state if they missed anything while offline.
RPC Flow
- A caller emits
rpc-callwith a method name and params. rpcHandler.tsresolves the roomrpc:<userId>:<method>.- The server looks for a target socket in that room.
- If no target is present, the server waits briefly for reconnect before failing.
- If a target is present, the server forwards the request with
rpc-request. - The target runs the handler through
RpcHandlerManagerand acks the result. - If the target disappears mid-call, the server fails the call instead of waiting for the full timeout.
This is how Happy does point-to-point control traffic on top of the same transport used for normal realtime sync.
Current Sharp Edges
packages/happy-agent/src/machineRpc.tsstill creates one-off caller sockets for machinespawnandresumeinstead of reusing a long-lived caller connection.packages/happy-server/sources/app/api/socket/rpcHandler.tsstill mixes room lookup, reconnect grace, mid-call presence checking, and metric emission in one place.
Debugging
If this path is flaky, the first things to check are:
- RPC success/failure rate
- RPC latency
- websocket connection churn
- Redis stream lag
Use multi-process.md for the deeper cross-replica and failure-mode details.