51 lines
3.0 KiB
Markdown
51 lines
3.0 KiB
Markdown
# 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.
|