Lumi/protocol/companion-protocol-v1.md
2026-07-22 11:01:49 +02:00

3.0 KiB

Lumi Companion Protocol v1

Status: experimental. The protocol is versioned independently from Lumi core, the server plugin, companion core, and companion transcription plugin.

Trust boundaries

The OBS bridge accepts only same-user named-pipe clients and speaks only the local IPC subset. The companion owns pairing and connects to Lumi over TLS WebSockets. Lumi authenticates a device before accepting a WebSocket upgrade. The bridge never receives a Lumi device credential and never connects to the network.

Server connection

  • Endpoint: wss://<lumi-host>/plugins/lumi_transcription/live
  • Header: Authorization: LumiDevice <device-id>.<device-secret>
  • Maximum structured message: 64 KiB.
  • Maximum binary audio message: 6,464 bytes (64-byte header plus up to 200 ms of PCM).
  • The client sends hello first. Lumi replies with hello_ack or closes with an application error.
  • Protocol v1 supports pcm_s16le; codec negotiation is still explicit so Opus can be added later.
  • Heartbeats use ping/pong. A new connection is a new session unless resume_session_id names a resumable session owned by that device.

Structured messages use the envelope in schemas/envelope.schema.json. Supported client events are hello, ping, source_update, obs_state, start, stop, and ack. Server events are hello_ack, pong, status, caption, metric, and error.

Binary PCM frame

All integers are little-endian. The fixed header is 64 bytes:

Offset Size Meaning
0 4 ASCII LACP
4 1 protocol version (1)
5 1 flags: active 0x01, muted 0x02
6 2 header bytes (64)
8 4 sequence number
12 8 monotonic capture timestamp, microseconds
20 16 session UUID bytes
36 16 OBS source UUID bytes
52 4 sample rate (16000)
56 2 channels (1)
58 2 PCM bits (16)
60 4 payload bytes

The payload is mono signed 16-bit little-endian PCM. Receivers reject malformed sizes and unsupported audio formats. Sequence gaps are reported; out-of-order frames are discarded. Every queue drops obsolete data rather than blocking a capture thread.

Local OBS IPC

Windows transport is a per-user named pipe named Lumi.Companion.ObsBridge.v1.<user-sid-hash>. Messages are length-prefixed (32-bit little-endian), capped at 64 KiB for JSON and 6,464 bytes for audio, and begin with a v1 handshake. The bridge accepts only this allowlist: hello, select_sources, caption, delivery_state, shutdown_notice, and PCM frames. It cannot execute arbitrary commands.

Caption events are revision-aware. Consumers retain the greatest revision for each (session_id, caption_id) and discard older revisions, including after reconnect.

Compatibility

Protocol v1 requires companion capability transcription.capture.v1 and server capability transcription.server.v1. OBS delivery is separately negotiated as obs.caption.native.v1; its absence keeps test-mode simulation available but blocks live delivery.