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

KindUse it when
Local streamYour page already receives the data (your own WebSocket, MQTT over WebSocket, a device API). Push samples in; nothing leaves the browser.
Relay streamData comes from devices or programs elsewhere. They encrypt and publish to the Kymoscope relay; your users’ browsers decrypt.

How relay streams stay private

  1. 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.
  2. Every batch of samples is encrypted with AES-256-GCM before it leaves the publisher.
  3. The relay forwards those bytes to viewers. It holds no key and cannot read or alter them.
  4. 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.