| 1 | # Connection runtime |
| 2 | |
| 3 | Web, the desktop renderer, and mobile share one connection owner per environment |
| 4 | in `packages/client-runtime`. Platform code supplies storage, credentials, network |
| 5 | signals, and application lifecycle events. React views consume the runtime. |
| 6 | Keeping retries and session lifetime here prevents competing reconnect loops when |
| 7 | several views need the same environment. |
| 8 | |
| 9 | ## One transport retry owner |
| 10 | |
| 11 | The [supervisor](../../packages/client-runtime/src/connection/supervisor.ts) owns |
| 12 | transport retry policy; resolving an endpoint and opening an RPC session are single |
| 13 | attempts. Transient failures retry with jittered exponential backoff, capped at |
| 14 | five minutes, that resets only after a connection stays up. Without jitter, every |
| 15 | client of a restarted server reconnects in the same second; with a short cap, a |
| 16 | client that can never connect retries all day. Offline states and authentication |
| 17 | failures wait for a wakeup instead of spending attempts on unchanged conditions. |
| 18 | |
| 19 | Foregrounding, an explicit retry, and an offline report probe the established |
| 20 | session, and only a failed probe reconnects. Offline reports are often wrong, for |
| 21 | example for a loopback server. A long mobile background suspension is the one |
| 22 | exception: it replaces the session at once, because the OS can kill a socket |
| 23 | without reporting closure, and a probe would hold a dead socket in "Resuming" |
| 24 | until it times out. That fresh attempt runs even while the network reports |
| 25 | offline. Foregrounding also wakes a pending retry immediately and |
| 26 | leaves an ordinary in-flight attempt alone. |
| 27 | |
| 28 | The [registry](../../packages/client-runtime/src/connection/registry.ts) scopes |
| 29 | connections by environment. An involuntary disconnect retains the registration |
| 30 | and cached data. Explicit removal closes the scope and clears credentials, |
| 31 | projections, and platform-owned state such as drafts. Cloud-account changes apply |
| 32 | to relay registrations; they must not discard directly paired environments. |
| 33 | |
| 34 | ## HTTP authorization |
| 35 | |
| 36 | RPC sessions authenticate at socket upgrade, while HTTP requests need current |
| 37 | credentials from the |
| 38 | [authorization service](../../packages/client-runtime/src/authorization/service.ts). |
| 39 | Replacing a healthy socket for HTTP renewal would interrupt conversations and |
| 40 | change the transport generation without a transport failure. Credential expiry |
| 41 | does not close the socket, and refresh failure belongs to the HTTP operation. |
| 42 | |
| 43 | Session listings must retain unrevoked connected sessions after credential expiry |
| 44 | so an open connection does not disappear from connection management. This does |
| 45 | not extend the credential's lifetime. New HTTP requests and socket upgrades still |
| 46 | require valid credentials. |
| 47 | |
| 48 | ## Transport health and data freshness are separate |
| 49 | |
| 50 | A socket opening is insufficient evidence that the environment is usable. The |
| 51 | [RPC session](../../packages/client-runtime/src/rpc/session.ts) waits for the |
| 52 | initial server configuration before becoming ready. Shell and thread data then |
| 53 | have their own synchronization state. A failed shell subscription can coexist |
| 54 | with a healthy connection; labeling that state "reconnecting" promises a |
| 55 | transport retry that will never happen. |
| 56 | |
| 57 | Cached projections remain readable offline. They must neither imply a live |
| 58 | connection nor overwrite newer live data during a reconnect. Loading and |
| 59 | resuming snapshots belongs to the shared state services, so every view agrees |
| 60 | on which data is current. |
| 61 | |
| 62 | [Thread detail](../../packages/client-runtime/src/state/threads.ts) separates |
| 63 | subscription lifetime from cache lifetime. Mounted consumers share one live |
| 64 | stream, which stops when the last consumer unmounts; hidden mounted routes still |
| 65 | count. A registry-local cache retains state and its replay cursor for five idle |
| 66 | minutes so back navigation can resume without another snapshot download. |
| 67 | |
| 68 | The desktop app adds one consumer: a |
| 69 | [keep-alive](../../apps/web/src/state/threads.ts) mounts every thread whose |
| 70 | session is starting or running, in each enabled environment. Opening a running |
| 71 | thread then needs no replay. The shell and detail streams are independent, so |
| 72 | the shell can report a stop before the detail loads or catches up. A stopped |
| 73 | thread stays mounted until its own stream is live and shows the stop, and the |
| 74 | stream then closes and saves the settled state. |
| 75 | Web and mobile do not keep threads alive. |
| 76 | |
| 77 | Retain state and cursor together only after an update finishes. Cancellation must |
| 78 | not advance the cached cursor beyond the applied data, and an old scope must not |
| 79 | overwrite its successor's cache. Preserve pagination data on reuse, but clear |
| 80 | canceled loading state. |
| 81 | |
| 82 | The [RPC boundary](../../packages/client-runtime/src/rpc/client.ts) resolves |
| 83 | requests against the current session at execution time. Durable subscriptions |
| 84 | follow replacement sessions. After a transport failure they wait for the |
| 85 | supervisor; an expected domain failure may resubscribe on the same healthy |
| 86 | session. Reconnection does not automatically replay mutations, whose retry and |
| 87 | idempotency rules belong to the operation. |