Voice room
Every war room has a voice room: everyone in the incident is in it on arrival, what is said is transcribed under the speaker's name, every joined agent reads the words, and the transcript folds into the postmortem. Two providers make the audio real: a media server that carries it between browsers, and a speech engine that turns a recording into text. Both are yours to choose; without them the room runs on a simulated token and a mock engine.
How it works
- Joining. Landfall signs a room token scoped to one incident, audio only, and the browser connects to the media server with it. Landfall itself never carries a media packet. The server's own speaker detection lights up the person talking.
- Recording needs consent. Nothing is captured until a participant ticks the consent box and starts a recording; the start and stop are on the incident timeline. While recording, the browser's own speech engine (Chrome, Edge, Safari) streams live captions under the speaker's name, and the microphone is kept as audio chunks with the retention window your organization sets.
- Transcribing a recording. A stored recording can be sent to the configured speech engine afterwards, which is the path for browsers without live captions (Firefox) and for a cleaner transcript. Segments land on the timeline like any other, and agents find them by search and in their updates.
The media server
Landfall adopts LiveKit, an open-source WebRTC SFU you can self-host or use hosted. Landfall needs the server's websocket URL and an API key pair to sign tokens with.
VOICE_MEDIA_PROVIDER=livekit LIVEKIT_URL=wss://livekit.example.com # what browsers connect to LIVEKIT_API_KEY=... # the pair Landfall signs room tokens with LIVEKIT_API_SECRET=...
For a laptop, pnpm dev:up starts a LiveKit container with its documented placeholder pair (devkey/secret) on ws://localhost:7880. On macOS, Docker Desktop's UDP proxy breaks WebRTC; run the binary natively instead (brew install livekit && livekit-server --dev), same port, same pair. Browsers need the server's ICE ports reachable: TCP 7881 and UDP 7882 locally, or whatever your deployment exposes.
The speech engine
Any service speaking the OpenAI speech API (POST /v1/audio/transcriptions). That is OpenAI itself, or a local Whisper server with no credential at all: speaches, faster-whisper-server, LocalAI. One setting picks it:
VOICE_STT_PROVIDER=openai-compatible VOICE_STT_URL=http://localhost:8000/v1 # or https://api.openai.com/v1 VOICE_STT_API_KEY= # empty for a local server VOICE_STT_MODEL=Systran/faster-whisper-tiny # or whisper-1
Audio capture also needs VOICE_AUDIO_ENABLED=true, which registers the recording routes and the retention sweep (see the voice retention settings under Settings → Governance). With it off, live captions still work; only the stored audio and the after-the-fact transcription are absent.
What agents see
A final transcript segment is a substantive item in every agent's context: it arrives through get_updates, it is found by search_context, and it is in the brief. Partial captions are never on the timeline and never reach an agent; they exist in the room and in browser memory only. Transcript text is data, never an instruction.
Defaults and safety
- Both providers default to their stand-ins (
simulated,mock). A provider is chosen by the setting, never by the presence of a credential. - The room token is audio only, one incident, no data channel, one hour.
- Recording is consent-gated; audio is kept for the organization's retention window and then deleted, with the deletion on the timeline.