Files
OSIT-AE-API-FastAPI/documentation/GUIDE__AE_API_V3_for_Frontend_websockets.md

7.8 KiB

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. Both are treated as opaque unique strings by the backend — no specific format is enforced.

wss://[api_domain]/v3/ws/group/{group_id}/client/{client_id}

group_id — identifies the shared channel (e.g., an event ID, a room name, or a Vision ID random string). All clients using the same group_id receive group-targeted messages together.

client_id — uniquely identifies this specific connection. The backend accepts any unique string (UUID, timestamp, Vision ID — no format validation). The recommended pattern is a UUID v4 generated once and persisted in localStorage so the same identity is reused across page reloads and sessions on that browser.

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=<your_app_key>
jwt Visa (user / account context) &jwt=<token>
x_account_id Alt account context &x_account_id=<account_id>

Full example URL:

wss://dev-api.oneskyit.com/v3/ws/group/{group_id}/client/{client_id}?api_key=<key>&jwt=<token>

C. Connection Example (TypeScript)

// client_id: generated once, persisted in localStorage for stable identity across reloads
if (!localStorage.getItem('controller_client_id')) {
    localStorage.setItem('controller_client_id', crypto.randomUUID());
}
const client_id = localStorage.getItem('controller_client_id')!; // UUID v4, e.g. "550e8400-e29b-41d4-a716-446655440000"

const group_id = "event_abc123"; // Any unique string identifying the shared channel
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.

{
  "msg_type": "msg",
  "target": "group",
  "msg": "Hello team!"
}

B. Direct Message (DM)

Sends the message to one specific client ID, regardless of their group.

{
  "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).

{
  "msg_type": "cmd",
  "target": "broadcast",
  "cmd": "MAINTENANCE_WARNING"
}

D. Echo

Sends the message back only to the sender (useful for testing round-trip latency).

{
  "msg_type": "msg",
  "target": "echo",
  "msg": "Ping!"
}

5. Specialized Message Types

Commands (cmd)

Used for remote control or orchestration.

{
  "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.
{
  "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:

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 unique string IDs: Both group_id and client_id in the path are opaque strings — any unique value works (timestamp, UUID, Vision ID random string). Just don't use raw database integer IDs. For to_id in direct messages, use whatever client_id that target client registered with.
  4. Listen for msg_type: Update your frontend handlers to switch logic based on the msg_type field instead of proprietary keys.