WebSocket Server
At the heart of the Gateway is a WebSocket server built on the ws library. It uses a protocol with three frame types: request, response, and event.
Protocol Frames
Source: src/gateway/protocol/index.ts
The Gateway defines three frame types. All frames are validated against JSON Schema using AJV:
// RequestFrame - Client → Server
interface RequestFrame {
id: string; // Request correlation ID
method: string; // e.g. "chat.send", "config.patch"
params?: unknown; // Method-specific parameters
}
// ResponseFrame - Server → Client
interface ResponseFrame {
id: string; // Matches request ID
ok: boolean; // Success flag
result?: unknown; // Response payload
error?: string; // Error message
meta?: unknown; // Additional metadata
}
// EventFrame - Server → Client (push)
interface EventFrame {
event: string; // Event name
data?: unknown; // Event payload
}The protocol supports versioning (PROTOCOL_VERSION) to ensure compatibility between client and server.
Server Implementation
Source: src/gateway/server.impl.ts
GatewayServer is the core runtime type that manages all connections and state:
// src/gateway/server.impl.ts (simplified)
interface GatewayServer {
config: GatewayConfig;
channels: ChannelRegistry;
agents: AgentRegistry;
plugins: PluginRegistry;
runtimeState: RuntimeState;
// Health snapshots, presence versions, agent run sequences...
}Client SDK
Source: src/gateway/client.ts
The GatewayClient class encapsulates the WebSocket connection from client to Gateway:
// src/gateway/client.ts (simplified)
interface GatewayClientOptions {
url: string; // ws://127.0.0.1:18789
credentials: {
token?: string;
password?: string;
};
instance: string; // Client instance identifier
mode: "operator" | "node"; // Connection role
scopes: string[]; // Permission scopes
capabilities: string[]; // Feature capabilities
}
class GatewayClient {
constructor(options: GatewayClientOptions);
// Protocol version negotiation
// Token/password auth
// TLS fingerprint validation
// Auto-reconnection handling
}Key client features:
- Protocol version negotiation
- Token / Password / Tailscale authentication
- TLS fingerprint validation
- Automatic reconnection
Method Dispatch
Source: src/gateway/server-methods.ts
handleGatewayRequest() is the single entry point for all requests. It routes each request to the appropriate handler module:
// src/gateway/server-methods.ts (simplified)
async function handleGatewayRequest(
method: string,
params: unknown,
options: GatewayRequestOptions
): Promise<void> {
// 1. Authorize the method call
const authResult = authorizeGatewayMethod(method, options);
if (!authResult.ok) {
return respond(false, null, authResult.error);
}
// 2. Look up handler in registry
const handler = coreGatewayHandlers[method];
if (!handler) {
return respond(false, null, `Unknown method: ${method}`);
}
// 3. Execute handler
await handler(params, options);
}Authorization Flow
Authorization dimensions:
| Dimension | Description | Example |
|---|---|---|
| Role | operator (admin) vs node (device) | Operators can modify config |
| Scope | admin, read, write, approvals, pairing | write scope required to send messages |
| Method-level | Each method declares required scope | config.patch requires admin |
Handler Modules
Source: src/gateway/server-methods/ (43 modules)
Each handler module exports a GatewayRequestHandlers-typed method map:
server-methods/
├── agent.ts # Agent management
├── agents.ts # Multi-Agent operations
├── browser.ts # Browser control
├── channels.ts # Channel state and lifecycle
├── chat.ts # Chat interactions
├── config.ts # Configuration read/write
├── connect.ts # Connection management
├── cron.ts # Cron jobs
├── device.ts # Device management
├── exec-approvals.ts # Execution approvals
├── health.ts # Health checks
├── logs.ts # Log queries
├── models.ts # Model management
├── nodes.ts # Node management
├── send.ts # Message sending
├── sessions.ts # Session operations
├── skills.ts # Skill management
├── system.ts # System information
├── talk.ts # Voice conversation
├── tts.ts # Text-to-speech
├── update.ts # Version updates
├── usage.ts # Usage statistics
├── voicewake.ts # Voice wake
├── web.ts # Web UI
└── wizard.ts # Setup wizardSend Handler
Source: src/gateway/server-methods/send.ts
The send handler supports idempotency deduplication:
// src/gateway/server-methods/send.ts (simplified)
const sendHandlers = {
async send(params, options) {
// 1. Validate params (to, message, channel, idempotencyKey)
// 2. Check idempotency deduplication
if (idempotencyKey && seen.has(idempotencyKey)) {
return respond(true, seen.get(idempotencyKey));
}
// 3. Resolve channel and outbound adapter
// 4. Deliver message via outbound pipeline
// 5. Cache result for idempotency
}
};Authentication Implementation
Source: src/gateway/auth.ts
Key security measures:
- Token comparison uses timing-safe comparison to prevent timing side-channel attacks
- Local connection detection (loopback + localhost validation)
- Tailscale integration verifies device identity via whois query
// src/gateway/auth.ts (simplified)
interface ResolvedGatewayAuth {
mode: "token" | "password";
// ...
}
async function authorizeGatewayConnect(
credentials: unknown,
auth: ResolvedGatewayAuth
): Promise<AuthResult> {
// Timing-safe token comparison
// Tailscale user verification via whois
// Fallback to token/password modes
}Summary
- The Gateway uses the
wslibrary with RequestFrame / ResponseFrame / EventFrame as its three frame types handleGatewayRequest()is the unified request entry point, performing role -> scope -> method-level authorization- 43 handler modules cover all functionality: chat, send, config, sessions, channels, and more
- Authentication supports Token, Password, and Tailscale modes with timing-safe comparison
- The client SDK (
GatewayClient) wraps connection management, protocol negotiation, and auto-reconnection
Next: Session Management