From 3f5f554595107318168427376c43edc9b850243e Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Wed, 11 Mar 2026 17:16:44 -0400 Subject: [PATCH] Now with websocket use instructions. :-) --- ...UIDE__AE_API_V3_for_Frontend_websockets.md | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 documentation/GUIDE__AE_API_V3_for_Frontend_websockets.md diff --git a/documentation/GUIDE__AE_API_V3_for_Frontend_websockets.md b/documentation/GUIDE__AE_API_V3_for_Frontend_websockets.md new file mode 100644 index 00000000..f757a8cb --- /dev/null +++ b/documentation/GUIDE__AE_API_V3_for_Frontend_websockets.md @@ -0,0 +1,192 @@ +# Aether API V3 WebSocket Integration Guide + +This guide explains how to implement real-time communication using the **Aether API V3 WebSocket** protocol. V3 introduces granular routing, strict message schemas, and improved multi-tenant isolation compared to previous versions. + +--- + +## 1. Key Improvements (V2 vs V3) + +| Feature | WebSocket V2 (Legacy) | WebSocket V3 (Modern) | +| :--- | :--- | :--- | +| **URL Prefix** | `/ws/` or `/ws_redis/` | `/v3/ws/` | +| **Routing** | **Global**: Every client receives every message. | **Granular**: Redis filters messages before sending. | +| **Performance**| Low efficiency at scale (Python filtering). | High efficiency (Redis native pub/sub). | +| **Schema** | Loose JSON objects. | Strict Pydantic-validated models. | +| **Presence** | None / Manual. | Automatic Redis-backed presence sets. | + +--- + +## 2. Connection Strategy + +### A. Endpoint URL +The V3 WebSocket path requires both a `group_id` and a `client_id` (using **Vision ID** random strings). + +```text +wss://[api_domain]/v3/ws/group/{group_id}/client/{client_id} +``` + +> Use `ws://` for local development and `wss://` in production (any HTTPS site). The Nginx config must include the Upgrade block — see Section 6. + +### B. Authentication +Browsers **cannot** set custom HTTP headers on WebSocket connections. Pass the API Key and account context as **query parameters** instead: + +| Parameter | Purpose | Example | +| :--- | :--- | :--- | +| `api_key` | Entry Ticket (machine auth) | `?api_key=` | +| `jwt` | Visa (user / account context) | `&jwt=` | +| `x_account_id` | Alt account context | `&x_account_id=` | + +**Full example URL:** +```text +wss://dev-api.oneskyit.com/v3/ws/group/{group_id}/client/{client_id}?api_key=&jwt= +``` + +### C. Connection Example (TypeScript) +```ts +const group_id = "group_abc123"; // Random Vision ID +const client_id = "device_xyz789"; // Random Vision ID +const api_key = import.meta.env.VITE_API_KEY; +const jwt = getSessionToken(); // your JWT helper + +const ws_url = `wss://dev-api.oneskyit.com/v3/ws/group/${group_id}/client/${client_id}?api_key=${api_key}&jwt=${jwt}`; + +const socket = new WebSocket(ws_url); + +socket.onopen = () => { + console.log("Connected to Aether WS V3"); +}; +``` + +--- + +## 3. The V3 Message Schema + +All messages sent and received over V3 must follow the standardized **WS_Message_V3** structure. + +### Message Fields +| Field | Type | Required | Description | +| :--- | :--- | :--- | :--- | +| `version` | string | Auto | Always `"3"`. Set by server. | +| `msg_type` | string | Yes | `'msg'`, `'cmd'`, `'heartbeat'`, `'presence'` | +| `target` | string | Yes | `'direct'`, `'group'`, `'broadcast'`, `'echo'` | +| `from_id` | string | Auto | **Server fills this from the URL path** — do not send. | +| `to_id` | string | Conditional | Target Client ID. Required when `target` is `'direct'`. | +| `group_id` | string | Auto | **Server fills this from the URL path** — do not send. | +| `cmd` | string | No | Specific action keyword (e.g., `'RELOAD'`). | +| `msg` | string | No | Human-readable text content. | +| `payload` | object | No | Flexible key-value data. | +| `sent_at` | string | Auto | ISO 8601 Timestamp. Set by server. | + +> **Frontend tip:** Only send `msg_type`, `target`, and whatever content fields you need (`msg`, `cmd`, `payload`, `to_id`). The server enforces `from_id`, `group_id`, and `sent_at` from the connection context, preventing spoofing. + +--- + +## 4. Message Targeting Logic + +V3 uses the `target` field to determine which Redis channel to use, ensuring only the intended recipients receive the data. + +### A. Group Broadcast +Sends the message to every client connected to the same `group_id`. +```json +{ + "msg_type": "msg", + "target": "group", + "msg": "Hello team!" +} +``` + +### B. Direct Message (DM) +Sends the message to one specific client ID, regardless of their group. +```json +{ + "msg_type": "msg", + "target": "direct", + "to_id": "target_client_random_id", + "msg": "Private message just for you." +} +``` + +### C. System Broadcast +Sends the message to **every** connected client on the platform (use sparingly). +```json +{ + "msg_type": "cmd", + "target": "broadcast", + "cmd": "MAINTENANCE_WARNING" +} +``` + +### D. Echo +Sends the message back only to the sender (useful for testing round-trip latency). +```json +{ + "msg_type": "msg", + "target": "echo", + "msg": "Ping!" +} +``` + +--- + +## 5. Specialized Message Types + +### Commands (`cmd`) +Used for remote control or orchestration. +```json +{ + "msg_type": "cmd", + "target": "group", + "cmd": "RELOAD_UI", + "payload": { "force": true } +} +``` + +### Heartbeats (`heartbeat`) +Keep the connection alive and **refresh presence** in the backend. Should be sent every 30-60 seconds. + +- The server intercepts `heartbeat` messages and refreshes the Redis presence TTL (1 hour window) before echoing back. +- Without periodic heartbeats, a client idle for >1 hour may disappear from the presence set even while still connected. +- Use `target: 'echo'` so the server sends the heartbeat straight back — useful for measuring round-trip latency. + +```json +{ + "msg_type": "heartbeat", + "target": "echo" +} +``` + +--- + +## 6. Infrastructure Requirements (Nginx) + +Unlike standard REST endpoints, WebSockets require explicit "Upgrade" handling in the Nginx gateway. If you are deploying to a new server, ensure the following block is present in your Nginx configuration: + +```nginx +location /v3/ws { + proxy_pass http://fastapi_backend; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $http_host; + proxy_read_timeout 2100s; # Match your app's max heartbeat/session time +} +``` + +--- + +## 7. Common Pitfalls & Troubleshooting + +- **HTTP 404 Errors**: This almost always means Nginx is missing the `location /v3/ws` block and is trying to serve the request as a static file from the disk. +- **HTTP 400 Errors**: Check your `Host` header. Nginx routes requests based on the `server_name` directive. If you connect to an IP or a non-standard hostname (like `localhost`), ensure it is explicitly listed in your Nginx config. +- **Connection Drops**: If the connection drops exactly after 60 seconds, check your Nginx `proxy_read_timeout`. It should be set high (e.g., `2100s`) to allow for long-lived WebSocket sessions. + +--- + +## 8. Migration Guide (V2 to V3) + +If you are upgrading from the legacy V2 WebSocket (`/ws/group/...`): + +1. **Change the URL**: Prepend `/v3/` to your WebSocket path. +2. **Wrap your JSON**: In V2, you might have sent `{"msg": "hi"}`. In V3, this must be `{"msg_type": "msg", "target": "group", "msg": "hi"}`. +3. **Use Vision IDs**: Ensure all IDs passed in the path and `to_id` fields are the random string IDs (`id_random`), not database integers. +4. **Listen for `msg_type`**: Update your frontend handlers to switch logic based on the `msg_type` field instead of proprietary keys.