LatteStream®

Quick Start

Getting Started

Building Custom SDKs

SDKs & Libraries

JavaScript/TypeScript

Node.js / Bun / Deno

Python

In Development

Go

In Development

PHP

In Development

API Reference

WebSocket API

REST API

Webhooks

Authentication

WebSocket API Reference

Complete protocol specification for building custom LatteStream SDKs

This document provides the complete WebSocket protocol specification for LatteStream. Whether you're building a custom SDK or integrating directly with the WebSocket API, this guide covers everything you need to know.

Overview

LatteStream uses a WebSocket protocol with enhanced performance and additional features. The protocol supports:

  • Real-time bidirectional communication
  • Public, private, and presence channels
  • Client-to-client messaging on private/presence channels
  • Automatic reconnection and heartbeat management
  • Both JSON and binary message formats - your SDK should support parsing both formats!

Connection Flow

1. Establish WebSocket Connection

wss://ws-{cluster}.lattestream.com

Clusters: eu1

2. Authenticate Connection

Immediately after the WebSocket connection opens, send an authentication message:

{ "api_key": "your-api-key-or-jwt-token" }

Supported key formats:

  • Public Keys (lspk_*): Requires discovery service (see Authentication)
  • Private Tokens (lsk_*): Direct encrypted API keys
  • JWT Tokens: Client tokens generated via /apps/token endpoint

3. Connection Established

Server responds with connection confirmation:

{ "event": "lattestream:connection_established", "data": { "socket_id": "123.456", "activity_timeout": 120, "protocol": 7 } }

Socket ID Format: {tenant_id}.{connection_id}

The socket_id is used for channel authorization on private/presence channels.

Message Format

All messages follow a standard JSON structure:

{ "event": "event_name", "channel": "channel_name", "data": { "key": "value" } }

Field Specifications:

  • event (required): Event name (max 200 characters)
  • channel (optional): Channel name for subscriptions/broadcasts (max 164 characters)
  • data (optional): Event payload (max 32KB)

Protocol Messages

Subscribe to Channel

{ "event": "lattestream:subscribe", "data": { "channel": "my-channel" } }

For private/presence channels, include auth token:

{ "event": "lattestream:subscribe", "data": { "channel": "private-chat", "auth": "lspc_encrypted_token" } }

Response (subscription succeeded):

{ "event": "lattestream_internal:subscription_succeeded", "channel": "my-channel", "data": {} }

Response (presence channel):

{ "event": "lattestream_internal:subscription_succeeded", "channel": "presence-room-1", "data": { "presence": { "ids": ["user1", "user2"], "hash": { "user1": { "name": "Alice" }, "user2": { "name": "Bob" } }, "count": 2 } } }

Unsubscribe from Channel

{ "event": "lattestream:unsubscribe", "data": { "channel": "my-channel" } }

Heartbeat (Ping/Pong)

Client sends:

{ "event": "lattestream:ping", "data": {} }

Server responds:

{ "event": "lattestream:pong", "data": {} }

Timing:

  • Send ping after 30 seconds of inactivity (configurable)
  • Activity timeout: 120 seconds (server will disconnect if no messages)
  • Reset activity timer on any message sent or received

Channel Types

Public Channels

Naming: No prefix (e.g., my-channel, chat-room-1)

Characteristics:

  • Accessible to all authenticated clients
  • No additional authorization required
  • Anyone can subscribe
  • Client events NOT allowed

Example:

{ "event": "lattestream:subscribe", "data": { "channel": "public-announcements" } }

Private Channels

Naming: private- prefix (e.g., private-user-123, private-chat)

Characteristics:

  • Require server-side authorization via auth endpoint
  • Client events allowed (event name must start with client-)
  • Suitable for user-specific or restricted channels

Authorization Flow:

  1. Client attempts to subscribe to private-user-123
  2. Client SDK calls your auth endpoint: POST /auth
  3. Your server validates the user's identity and returns auth token
  4. Client SDK includes auth token in subscribe message

Subscribe Example:

{ "event": "lattestream:subscribe", "data": { "channel": "private-chat-room", "auth": "lspc_abc123..." } }

Presence Channels

Naming: presence- prefix (e.g., presence-room-1, presence-game-lobby)

Characteristics:

  • Require server-side authorization (like private channels)
  • Automatic member tracking
  • Member join/leave events
  • Client events allowed
  • Access to member list with user info

Subscribe Example:

{ "event": "lattestream:subscribe", "data": { "channel": "presence-game-lobby", "auth": "lspc_abc123..." } }

Presence Events:

Member joined:

{ "event": "lattestream_internal:member_added", "channel": "presence-room-1", "data": { "user_id": "user123", "user_info": { "name": "Alice", "avatar": "https://..." } } }

Member left:

{ "event": "lattestream_internal:member_removed", "channel": "presence-room-1", "data": { "user_id": "user123", "user_info": { "name": "Alice" } } }

Channel Naming Rules

Valid characters: Alphanumeric, hyphens, underscores

  • Examples: my-channel, user_123, chat-room-1

Case sensitivity: Channel names are case-sensitive

  • My-Channel and my-channel are different channels

Maximum length: 164 characters

Reserved prefixes:

  • private-: Private channels
  • presence-: Presence channels
  • lattestream:: Protocol messages (reserved)
  • lattestream_internal:: Internal events (reserved)

Broadcasting Events

Receiving Broadcast Messages

When a server triggers an event or a client sends a client event, you receive:

{ "event": "new-message", "channel": "chat-room", "data": { "user": "Alice", "message": "Hello, world!" } }

Client Events

Requirements:

  • Only allowed on private (private-*) and presence (presence-*) channels
  • Event name must be prefixed with client-
  • Do NOT echo back to the sender

Sending client event:

{ "event": "client-typing", "channel": "private-chat", "data": { "user": "Alice", "status": "typing" } }

Error response (if client event on public channel):

{ "event": "lattestream:error", "data": { "code": 4301, "message": "Client events are only allowed on private and presence channels" } }

Error response (if event doesn't start with client-):

{ "event": "lattestream:error", "data": { "code": 4201, "message": "Client events must be prefixed with 'client-'" } }

Error Handling

Error Message Format

{ "event": "lattestream:error", "data": { "code": 4009, "message": "Unauthorized to access channel" } }

Error Codes

CodeNameDescriptionAction
4001Generic subscription errorGeneral subscription failureCheck channel name and auth
4004Maximum channels exceededToo many subscriptions per clientUnsubscribe from unused channels
4005Invalid channel nameChannel name format invalidCheck naming rules
4009UnauthorizedAuthentication failedVerify auth token
4201Invalid eventClient event must be prefixed with client-Add client- prefix
4301Client event rejectedClient events only on private/presenceUse private/presence channel

String Error Codes

Some errors use string codes in the code field:

  • subscription_limit: Too many channel subscriptions
  • invalid_event: Event name validation failed
  • client_event_rejected: Client events not allowed on this channel type
  • not_subscribed: Attempted operation on unsubscribed channel
  • unauthorized: Channel authorization failed
  • channel_not_found: Channel does not exist
  • invalid_request: Request format is invalid

Rate Limits & Constraints

Connection Limits

Per Client:

  • Default: 100 channels per connection
  • Configurable via LATTESTREAM_MAX_CHANNELS_PER_CLIENT
  • Error code 4004 when exceeded

Per Tenant:

  • Configured in tenant metadata
  • Max concurrent connections per tenant

Per Node:

  • 100,000+ concurrent connections supported
  • Optimized for low-resource environments (0.5 vCPU tested)

Message Limits

Message Size:

  • Event data: 32KB per message
  • Channel name: 164 characters max
  • Event name: 200 characters max

Channel Buffer:

  • 1024 messages per channel
  • 8000 messages per batch

Bandwidth Rate Limiting

Throttling Behavior:

  • Warning: 80% of allocated bandwidth
  • Throttle: 90% - begins delaying messages (50ms-1000ms)
  • Drop: 100% - starts dropping messages
  • Hard limit: 120% - hard blocking

Queue Timeout: 5000ms

Timeouts

Activity Timeout: 120 seconds (default)

  • No messages sent or received
  • Server disconnects idle clients

Ping Interval: 30 seconds (recommended)

  • Send ping after inactivity
  • Reset on any message

Pong Timeout: 30 seconds

  • Disconnect if no pong received

Binary Protocol

LatteStream supports optimized binary messages for performance-critical applications.

Frame Structure:

[4 bytes: type][4 bytes: length][payload]

Message Types:

  • 0x01: JSON payload (UTF-8 encoded)
  • 0x02: Binary payload with JSON metadata
  • 0x03: Compressed JSON payload

Note: Most SDKs use text frames (JSON) by default. Binary protocol is optional for optimization.

Advanced Features

Message Batching

For high-throughput scenarios, batch multiple messages:

{ "event": "lattestream:batch", "data": { "messages": [ { "event": "lattestream:subscribe", "data": { "channel": "channel-1" } }, { "event": "lattestream:subscribe", "data": { "channel": "channel-2" } } ] } }

Connection States

Implement these connection states in your SDK:

  1. connecting - Establishing WebSocket connection
  2. connected - Successfully connected and authenticated
  3. disconnected - Connection lost
  4. unavailable - Connection unavailable (retry exhausted)
  5. failed - Connection failed permanently

Performance Characteristics

Latency:

  • Sub-millisecond message delivery
  • Optimized for <1ms p99 latency
  • Lock-free broadcast architecture

Throughput:

  • 10 Gbps+ per node
  • Zero-copy operations where possible
  • SIMD-accelerated JSON parsing

Scalability:

  • 100,000+ connections per node
  • Sharded connection pool
  • Lock-free subscription manager

SDK Implementation Guidelines

When building a custom SDK, implement:

  1. Connection Management

    • WebSocket connection lifecycle
    • Automatic authentication on connect
    • Connection state tracking
  2. Reconnection Logic

    • Exponential backoff (see SDK Implementation Guide)
    • Jitter to prevent thundering herd
    • Auto-resubscribe channels on reconnect
  3. Channel Management

    • Subscribe/unsubscribe operations
    • Channel-specific event binding
    • Auth flow for private/presence channels
  4. Event Handling

    • Event emitter pattern
    • Separate global and channel events
    • Error isolation (listener errors don't crash SDK)
  5. Heartbeat

    • Activity timer implementation
    • Automatic ping on inactivity
    • Pong timeout handling
  6. Error Handling

    • Parse error codes and messages
    • Emit error events to application
    • Graceful degradation

See the SDK Implementation Guide for detailed patterns and best practices from the official TypeScript/JavaScript SDK.

Example: Complete Connection Flow

// 1. Open WebSocket connection const ws = new WebSocket('wss://ws-us-east-1.lattestream.com'); // 2. On connection open, send authentication ws.onopen = () => { ws.send( JSON.stringify({ api_key: 'lsk_your_private_token', }) ); }; // 3. Handle messages ws.onmessage = (event) => { const message = JSON.parse(event.data); if (message.event === 'lattestream:connection_established') { console.log('Connected! Socket ID:', message.data.socket_id); // 4. Subscribe to channel ws.send( JSON.stringify({ event: 'lattestream:subscribe', data: { channel: 'my-channel' }, }) ); } if (message.event === 'lattestream_internal:subscription_succeeded') { console.log('Subscribed to:', message.channel); } // Handle your custom events if (message.event === 'new-message') { console.log('Received:', message.data); } // Handle ping/pong if (message.event === 'lattestream:ping') { ws.send(JSON.stringify({ event: 'lattestream:pong', data: {} })); } }; // 5. Handle errors and disconnection ws.onerror = (error) => { console.error('WebSocket error:', error); }; ws.onclose = () => { console.log('Disconnected, attempting to reconnect...'); // Implement reconnection logic here };

Related Documentation

Support


Next Steps: Learn about Authentication to implement secure channel access in your custom SDK.