You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/architecture.md
+44-12Lines changed: 44 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -206,15 +206,45 @@ Full spec: [`.cursor/controllerv3.8/docs/15-fog-platform-reconcile.md`](../.curs
206
206
207
207
## WebSocket exec & log sessions
208
208
209
-
Interactive **exec** and **log streaming** use paired WebSocket sessions between operators (Bearer JWT), Controller, and Edgelet agents (fog token). Plan 16 hardens log sessions and shared WS infra (HA, drain, OTEL). **Plan 17** redesigns **microservice exec** to log-style multi-session flow (3 concurrent per MS, agent poll + session-scoped WS) — **Edgelet agent wire change required** for exec (see [edgelet-invariants.md §10.1](../.cursor/controllerv3.8/docs/edgelet-invariants.md)).
209
+
Interactive **exec** and **log streaming** use paired WebSocket sessions between operators (Bearer JWT), Controller, and Edgelet agents (fog token). Plan 16 hardens log sessions and shared WS infra (HA, drain, OTEL). **Plan 17** redesigns **microservice exec** to log-style multi-session flow (3 concurrent per MS, agent poll + session-scoped WS). **Plan 18** production-hardens cross-replica relay via **`WsRelayTransport`** — AMQP pool + recovery when `nats.enabled=false`, NATS Core when `nats.enabled=true` (R102–R113). **Edgelet agent wire change required** for exec only (see [edgelet-invariants.md §10.1](../.cursor/controllerv3.8/docs/edgelet-invariants.md)).
| Log content | Live relay only — no log line persistence; audit connect/disconnect |
266
-
| HA relay | Cross-replica sessions **require**AMQP (`WebSocketQueueService`); same-replica may use direct WS; **fail fast** when router down|
296
+
| HA relay | Cross-replica sessions **require**a **relay backend** (R112): **AMQP** router queues when `nats.enabled=false`; **NATS Core** subjects on hub when `nats.enabled=true`. Same-replica may use direct WS; **fail fast**close **1013**when active backend unavailable|
267
297
| Graceful drain |**30s** on SIGTERM / k8s `preStop` — CLOSE frames, queue cleanup, session row delete |
268
298
| Security | Agent handlers validate fog token **before** message processing; **50** upgrades/min/IP; **100** active WS/IP; JWT in `?token=` (ingress log redaction required) |
**Relay transport (Plan 18, R102):** Selected once at startup from existing `nats.enabled` / `NATS_ENABLED` — **no new relay env var**. `false` → AMQP pool (8 connections, sticky by `sessionId`); `true` → NATS Core on hub with **`controller`** NATS account. Relay connect is **lazy** — does not block Controller startup.
Copy file name to clipboardExpand all lines: docs/operations/ws-sessions.md
+43-9Lines changed: 43 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,7 +6,7 @@
6
6
7
7
## Overview
8
8
9
-
Controller exposes **interactive exec** and **log streaming** over WebSocket on the API port (default **51121**). Sessions pair an operator browser/CLI client (Bearer JWT) with an Edgelet agent (fog token). In multi-replica deployments, cross-replica relay requires the**Skupper-style AMQP router** microservice.
9
+
Controller exposes **interactive exec** and **log streaming** over WebSocket on the API port (default **51121**). Sessions pair an operator browser/CLI client (Bearer JWT) with an Edgelet agent (fog token). In multi-replica deployments, cross-replica relay uses a**relay backend** selected at startup by **`nats.enabled`** (Plan 18, R102): **AMQP** router queues when `false`, **NATS Core** on the platform hub when `true`.
10
10
11
11
---
12
12
@@ -38,18 +38,41 @@ Without redaction, long-lived bearer tokens may appear in load balancer logs.
38
38
39
39
## Multi-replica HA
40
40
41
+
Relay transport is selected **once at startup** from existing platform config — **no separate relay env var** (R102):
42
+
43
+
|`nats.enabled`| Cross-replica relay backend |
44
+
|----------------|----------------------------|
45
+
|`false` (default) |**AMQP** — Skupper-style router queues via `WebSocketQueueService`|
46
+
|`true`|**NATS Core** — hub pub/sub subjects `controller.relay.v1.*` via `NatsRelayTransport`|
47
+
48
+
Set `NATS_ENABLED=true` only when the platform NATS hub is deployed and all Controller replicas share the same value.
| Fail fast when relay backend down |`true`| `WS_HA_FAIL_FAST_ON_ROUTER_UNAVAILABLE |
45
54
46
-
**Requirements:**
55
+
> Env names retain `AMQP`/`ROUTER` for backward compatibility; semantics apply to the **active relay backend** (AMQP or NATS) per R112.
47
56
48
-
1. Deploy the **router** system microservice and ensure Controller can reach AMQP (`RouterConnectionService`).
57
+
### AMQP relay (`nats.enabled=false`)
58
+
59
+
1. Deploy the **router** system microservice and ensure Controller can reach AMQP (`RouterConnectionManager` pool).
49
60
2. Run **2+ Controller replicas** behind a load balancer with **sticky sessions optional** — cross-replica exec/log uses AMQP queues (`agent-{sessionId}`, `user-{sessionId}`, `logs-user-{sessionId}`).
50
-
3. When the router is unavailable, new cross-replica sessions close with WebSocket code **1013** (`Router unavailable for cross-replica session`).
61
+
3. When the router/AMQP backend is unavailable, new cross-replica sessions close with WebSocket code **1013** (`Router unavailable for cross-replica session`).
62
+
63
+
Plan 18 adds an **8-connection AMQP pool** per replica with overflow recovery — intense log streams must not poison other sessions (no router restart required). **Remote CP** resolves **`router.default.svc.bridge.local`** then default router `host`; **Kubernetes CP** resolves **`router.{namespace}.svc.cluster.local`** then default router `host`. Port from `Routers.messagingPort` (default **5671**).
51
64
52
-
Same-replica sessions may relay directly without AMQP when both user and agent land on the same pod.
65
+
### NATS relay (`nats.enabled=true`)
66
+
67
+
1. Platform NATS hub must be running with `NatsInstances.isHub=true`.
3. Cross-replica exec uses subjects `controller.relay.v1.exec.{sessionId}.agent` / `.user`; logs use `controller.relay.v1.log.{sessionId}.user`. Plain TCP to hub — port from `NatsInstances.serverPort` (default **4222**) for every host in the resolver list.
70
+
4.**Remote CP:** Controller resolves **`nats.default.svc.bridge.local`** (Edgelet internal DNS) then hub `host`; both use hub `serverPort`.
71
+
5.**Kubernetes CP:** Controller resolves **`nats-server.{namespace}.svc.cluster.local`** then hub `host`.
72
+
6. Remote ControlPlane replicas connect to the **hub** NATS only — not local fog NATS leaf.
73
+
7. When NATS relay is unavailable, fail-fast semantics match AMQP (close **1013** when configured).
74
+
75
+
Same-replica sessions may relay directly without AMQP or NATS when both user and agent land on the same pod.
53
76
54
77
---
55
78
@@ -59,7 +82,7 @@ On shutdown, Controller drains WebSocket sessions for up to **`WS_DRAIN_TIMEOUT_
59
82
60
83
1. Reject new upgrades (`verifyClient` → draining).
61
84
2. Close pending users with code **1001** (`Server draining`).
62
-
3. Send CLOSE frames, clean exec/log session DB rows, tear down AMQP bridges.
85
+
3. Send CLOSE frames, clean exec/log session DB rows, tear down relay bridges (AMQP or NATS).
The `--multi-ms` mode creates **3 exec sessions per microservice** (100 MS × 3 = 300 pairs) to validate multi-session pairing latency under the same p99 SLO.
115
138
139
+
**AMQP profile** (`nats.enabled=false`): run the probe above on a dev machine — it exercises in-process `ExecSessionManager` pairing only (no router required). Record p99 from stdout; target **< 5000 ms**.
140
+
141
+
**NATS profile** (`nats.enabled=true`): the same probe validates session-manager pairing latency (transport-agnostic SLO). For end-to-end NATS relay validation in staging:
142
+
143
+
1. Deploy Controller with **`NATS_ENABLED=true`** on **2+ replicas** and a platform NATS hub (`NatsInstances.isHub=true`).
3. Run cross-replica exec/log sessions (user on replica A, agent on replica B) while recording OTEL **`ws_pairing_duration_ms`** p99.
146
+
4. Optionally repeat `node test/load/ws-pairing-load.js --pairs 500` against staging API with agent simulators — same **p99 < 5s** SLO applies.
147
+
116
148
For production validation, repeat against a staging cluster with real agent simulators and record p99 from Controller OTEL histogram `ws_pairing_duration_ms`.
Cross-replica sessions fail fast with close code **1013** when router is unavailable.
2777
+
**HA (R112):** Multi-replica deployments require a **relay backend** selected at startup by **`nats.enabled`**: AMQP router queues when `false` (default), NATS Core pub/sub on the platform hub when `true`. Cross-replica sessions fail fast with close code **1013** when the active relay backend is unavailable. See `docs/operations/ws-sessions.md`.
2779
2778
2780
2779
See `#/components/schemas/WsExecMessageTypes` and `#/components/schemas/WsCloseCodes`.
2781
2780
operationId: userMicroserviceExecWebSocket
@@ -6593,11 +6592,16 @@ paths:
6593
6592
tags:
6594
6593
- NATS
6595
6594
summary: Gets NATS creds for specific account user
6595
+
description: |
6596
+
Returns base64-encoded `.creds` file content for the user.
6597
+
6598
+
Path `{appName}` is an **application name** for app-linked accounts, or a **NATS account name** for platform/system accounts (`SYS`, leaf accounts, **`controller`**).
6596
6599
operationId: getNatsUserCreds
6597
6600
parameters:
6598
6601
- in: path
6599
6602
name: appName
6600
6603
required: true
6604
+
description: Application name or NATS account name (e.g. `SYS`, `controller`)
6601
6605
schema:
6602
6606
type: string
6603
6607
- in: path
@@ -7033,7 +7037,7 @@ tags:
7033
7037
- name: WebSocketSessions
7034
7038
description: |
7035
7039
Interactive exec and log streaming WebSocket endpoints (MessagePack binary).
7036
-
Multi-replica HA requires AMQP router — see operations/ws-sessions.md.
7040
+
Multi-replica HA requires a relay backend per **`nats.enabled`** (AMQP when `false`, NATS Core when `true`) — see operations/ws-sessions.md.
7037
7041
- name: User
7038
7042
description: Manage your users
7039
7043
- name: Secrets
@@ -8276,7 +8280,11 @@ components:
8276
8280
description: True when microservice is the ControlPlane controller workload (DB column)
8277
8281
ControllerRegisterRequest:
8278
8282
type: object
8279
-
description: Slim register body for Edgelet ControlPlane controller workload
8283
+
description: >-
8284
+
Edgelet ControlPlane controller workload register body. Accepts the same
8285
+
container/workload fields as user microservice deploy (excluding application,
8286
+
serviceAccount, and natsConfig). Ownership fields (iofogUuid, application)
0 commit comments