Live streams
Live streams
Two kinds of live data: streams your page feeds itself, and end-to-end encrypted relay streams.
Local and relay streams
| Kind | Use it when |
|---|---|
| Local stream | Your page already receives the data (your own WebSocket, MQTT over WebSocket, a device API). Push samples in; nothing leaves the browser. |
| Relay stream | Data comes from devices or programs elsewhere. They encrypt and publish to the Kymoscope relay; your users’ browsers decrypt. |
How relay streams stay private
- The publisher’s API key carries a secret part that never leaves the publisher (a stream password can be used instead). The key is derived from it with PBKDF2 (600,000 iterations) and a random salt.
- Every batch of samples is encrypted with AES-256-GCM before it leaves the publisher.
- The relay forwards those bytes to viewers. It holds no key and cannot read or alter them.
- The viewer’s browser derives the same key from the API key (or password) and decrypts. A wrong one is detected before any data is shown.
Performance
- Each stream keeps its samples in a columnar ring buffer: up to 10 million per stream, within 512 MB (8 bytes per sample per channel, time included); past that the oldest are dropped. Memory grows with the samples received. Set both for every stream, or per stream:
TypeScript createEngine({ live: { capacity: 50_000_000, maxMemoryMB: 2048 } }) engine.live.openLocal('Bench rig', { capacity: 2_000_000 }) await engine.live.connect(stream, apiKey, { maxMemoryMB: 1024 }) - Charts keep up at any buffer size: a window following the live edge only processes the samples that arrived since the last frame, and a window in the history only reads its own samples.
- Multi-band charts read the buffer every animation frame, outside React, so a steady stream does not re-render your app.
- A stream is marked stale after 4 seconds without data and live again when samples resume.
- Publishers batch samples; only one message per batch crosses the network.