Appearance
Browser boot
This is the network sequence a fresh browser tab goes through to reach "Matrix client connected, app rendered" state.
Sequence
1. GET /apps/chat/ → index.html (from chat.http asset endpoint)
2. GET /apps/chat/assets/index-abc123.js → main bundle (asset endpoint)
3. GET /apps/chat/assets/style-def456.css → stylesheet (asset endpoint)
4. GET /api/bootstrap → bootstrap JSON (gateway-direct)
5. WS /nats-ws (CONNECT after upgrade) → NATS WebSocket (gateway upgrade or Caddy)
6. RR on local NATS: subscribe, mount actors, register components
7. First app render with live dataSteps 1-3 are static asset fetches handled by MatrixHttpAssetEndpointActor in the package's runtime, relayed through the gateway.
Step 4 is the dynamic edge — the gateway's own platform endpoint.
Step 5 is the WebSocket upgrade to NATS.
Steps 6+ happen entirely over NATS; the gateway is no longer involved.
Step 1: index.html
The browser navigates to /apps/chat/. The gateway resolves it as a static-app route, asks chat.http for index.html, and returns the bytes.
The HTML typically contains:
html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/apps/chat/assets/style-def456.css">
<script type="module" src="/apps/chat/assets/index-abc123.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>References use absolute paths because:
- The package's build emits absolute
/apps/chat/...URLs. - For Space routes (
/<routeKey>/chat/...), the gateway rewrites these prefixes at serve time (rewriteWebappHtmlResponse) so the URLs in the rendered HTML match the user-visible URL.
Step 2-3: hashed asset bundles
Most package builds (Vite-based) emit content-hashed asset names: index-abc123.js, style-def456.css. The hash changes when content changes, so the browser's cache works correctly even with the gateway's cache-control: no-cache (the URL itself is the version).
For non-hashed assets, the no-cache header forces the browser to revalidate on each load. See Cache and invalidation.
Step 4: /api/bootstrap
The browser fetches /api/bootstrap to get the dynamic configuration:
json
{
"natsWsUrl": "wss://hivecast.ai/nats-ws",
"root": "COM.NIMBLETEC.RICHARD-SANTOMAURO",
"transportPlan": {
"wsPath": "/nats-ws",
"authMode": "jwt",
"authEndpoint": "/api/nats-jwt"
},
"principal": { "principalId": "...", "displayName": "Richard" },
"busToken": "..."
}This response is per-request, derived from cookies/session/host config. See App bootstrap.
Step 5: NATS WebSocket
The browser uses the bootstrap result to open a WebSocket. For authMode: 'jwt', it first calls authEndpoint (/api/nats-jwt) to fetch a NATS-protocol JWT. Then it opens wss://<origin>/nats-ws, sends the NATS CONNECT frame with the JWT, and is connected.
After CONNECT, NATS protocol takes over. The gateway sees only the WebSocket upgrade; it does not parse NATS frames.
Step 6: Mount components
The browser-side Matrix runtime instantiates a MatrixRuntime, which:
- Creates a unique
runtimeId(browser-tab-scoped). - Subscribes to its
<runtimeId>.$inboxsubject. - Mounts the package's browser-side components (e.g.
chat.ui). - Registers each component as a provider in
system.registry. - Begins heartbeating on the registry.
The gateway is not involved in any of this. It happens entirely over NATS.
Step 7: First render
The browser-side component fetches initial data via Matrix RR:
typescript
await RequestReply.execute(context, 'chat', 'chat.thread.list', {});
// → { ok, threads: [...] }The headless chat actor (in the package's runtime) responds. The browser renders.
Total round trips
For a fresh load with cold caches:
- 1× HTML
- N× JS/CSS bundles (depends on the app)
- 1× /api/bootstrap
- 1× WebSocket upgrade + 1× NATS auth
- 1× first data RR
For a cached SPA navigation within the same session, only the data RR runs.
Failure surfaces
| Step | Failure | Symptom |
|---|---|---|
| 1 | 404 from gateway | Wrong appName or runtime down |
| 4 | 500 from /api/bootstrap | Gateway misconfig; check logs |
| 5 | WS handshake fails | NATS WS port unreachable; check transport.nats.wsPort |
| 5 | NATS CONNECT auth fails | JWT/token mismatch; check session and credentialsRef |
| 6 | Components don't mount | bus permissions; check NATS account permissions |
See also
Source:
projects/matrix-3/packages/system-gateway-http/src/host-platform-http-surface.ts:480-580.