Configuration and modules
The launcher reads one explicit environment file. That file takes precedence over environment variables inherited from a developer shell, reducing accidental connections to another database or bucket.
bun run deploy modulesbun run deploy config --env-file deploy/.env.stagingCOMPOSE_PROFILES selects services. The default staging selection is core,world,automation,monitoring,voice.
| Profile | Services | Additional configuration |
|---|---|---|
core |
Mainframe, Postgres, SRS, indexer, Caddy | Core secrets, storage, admin account, public IP |
storage |
Local MinIO and bucket initialization | Use for a local deployment, not the managed staging bucket |
world |
world-sim | Admin token, world configuration, advertised admin URL |
automation |
auto-operator | Mode, serial selection, provider selection, battery thresholds |
monitoring |
Grafana, Prometheus, host/container/Postgres exporters, Loki, Alloy | Grafana admin credentials |
feeds |
Weather and radar-net | Weather sites and upstream connectivity |
detections |
Vision module | OpenRouter key and model roster; incurs model usage |
assistant |
On-page assistant (model proxy) | OPENROUTER_API_KEY, ASSISTANT_MODELS (first is the default; verify the ids), ASSISTANT_MAX_CONCURRENT; incurs model usage per turn |
interop |
TAK bridge | CA, server certificate, key, and client certificates |
autel |
Native Autel controller host and its authenticated MQTT broker | Provisioned controller bindings (AUTEL_NATIVE_DIR/native.json) |
vanagas |
Read-only observation of vanago-akis drones | Its broker credentials and upstream SRS addresses |
vanagas-phones |
Phone location, audio/video and recordings | Shared Vanagas broker/SRS config plus dedicated snapshot URL/key |
voice |
LiveKit, Redis and Egress for voice rooms and optional recording | LIVEKIT_API_KEY, LIVEKIT_API_SECRET; 7881/tcp and 7882/udp open to PUBLIC_IP |
autel and vanagas are separate modules with separate provider identities (MAINFRAME_AUTEL_API_KEY, MAINFRAME_VANAGAS_API_KEY) and can run together: real controllers on the native host while another system’s flights are observed. The launcher builds MAINFRAME_ENABLED_MODULES from the selected profiles. API keys remain stable across restarts; changing a configured value does not rotate an existing database key automatically.
Deployment values
Section titled “Deployment values”For a sharded vanago-akis upstream, set VANAGAS_SRS_SOURCES to a JSON array of API/RTMP pairs, one per shard. For example:
VANAGAS_SRS_SOURCES='[{"api":"http://srs-1.example.com:1985","rtmp":"rtmp://srs-1.example.com:1935/live"},{"api":"http://srs-2.example.com:1985","rtmp":"rtmp://srs-2.example.com:1935/live"}]'This takes precedence over the single VANAGAS_SRS_API/VANAGAS_SRS_RTMP pair. The Vanagas container must reach each API; the Mainframe source adapter must reach each RTMP address, including private DNS/VPN routes. /api/health reports per-shard discovery health and each relay’s source server. Discovery outages preserve existing pulls while healthy shards continue updating. Duplicate stream names without a previously selected owner are refused. This observer does not request new publishing assignments or send upstream controller commands.
| Setting | Meaning |
|---|---|
MAINFRAME_BASE_URL |
Mainframe’s public authentication origin |
MAINFRAME_PUBLIC_URL |
Frontend build-time default connection URL |
MAINFRAME_TRUSTED_ORIGINS |
Browser origins allowed to carry credentials; also the passkey origins |
MAINFRAME_SSO_ISSUER, MAINFRAME_SSO_CLIENT_ID, MAINFRAME_SSO_CLIENT_SECRET |
The OIDC identity provider for single sign-on; read only when Settings → Security → Single sign-on is on at boot |
PUBLIC_IP |
SRS’s browser-reachable WebRTC address |
MEDIA_RTC_PORT |
UDP and TCP media port (8189 by default; staging uses 8000) |
MEDIA_SEGMENT_DURATION |
Minimum archive segment duration, default 60s |
MEDIA_RTMP |
Publishing address reachable by providers and controllers |
LIVEKIT_API_KEY, LIVEKIT_API_SECRET |
The voice server’s key pair; Mainframe mints join tokens with it and verifies its webhooks |
LIVEKIT_PUBLIC_URL |
The signalling address browsers dial; defaults to wss://DOMAIN (Caddy proxies /rtc to LiveKit) |
LIVEKIT_STUN_SERVERS |
JSON list of STUN servers clients receive; empty keeps stun.l.google.com:19302, [] for a LAN box without internet |
BLOB_S3_* |
Private object-store endpoint, region, bucket, and credentials |
WORLD_SIM_PUBLIC_URL |
Base URL for the simulator admin link |
AUTO_OPERATOR_MODE |
Separate module behavior: suggestions or automatic mission dispatch |
AUTO_OPERATOR_SERIALS |
Exact comma-separated serial allowlist |
AUTO_OPERATOR_SERIAL_PREFIX |
Optional literal serial prefix; no wildcard syntax |
AUTO_OPERATOR_PROVIDER_IDS |
Provider identities allowed by the serial selection |
Exact serials and a prefix form an OR selection, then the provider restriction applies. Empty serial selection permits no aircraft. Automatic mode refuses to start without a selection.
Voice media takes two more host ports beside MEDIA_RTC_PORT: 7881/tcp (ICE over TCP, the fallback for networks that drop UDP) and 7882/udp (one multiplexed port for every participant). LiveKit advertises PUBLIC_IP as its only candidate, so a wrong value means one-way or no audio. The launcher sets LIVEKIT_URL for Mainframe only while the voice profile is selected; without it /api/voice/config answers enabled: false and the Voice panel says so. Microphones need a secure context: a LAN deployment reached over plain http:// by IP has no microphone, so serve Terminal over Caddy’s internal CA (install it on the laptops) or a real certificate.
Changes to deployment environment values require container recreation. Changing MAINFRAME_PUBLIC_URL also requires rebuilding the frontend image. Document edits made through Terminal take effect through Mainframe’s document watches.
Initial values
Section titled “Initial values”These environment values seed Mainframe’s settings documents on the first boot of a database and are never read again. Change them afterwards in Terminal → Settings → Mainframe (administrators), where every change is audited; a variable edited in the environment after the first boot has no effect. The Settings page describes each section.
| Variable | Initial value of | Settings section |
|---|---|---|
MAINFRAME_AUTONOMY |
Autonomy (suggest or auto) |
Operations |
MAINFRAME_STATE_RETENTION_DAYS |
State horizon (90 days) | Retention |
MAINFRAME_TELEMETRY_RETENTION_DAYS |
Telemetry horizon (14 days) | Retention |
MAINFRAME_BULK_RETENTION_HOURS |
Bulk horizon (48 hours) | Retention |
MAINFRAME_MEDIA_RETENTION_HOURS |
Recording retention (168 hours) | Media |
mainframe.config.ts → media |
Default record policy and overrides | Media |
The launch battery minimum (30 %), the coverage grid (150 m cells, 10 minute horizon, 300 m border strip, the first geofence and border markings), the workspace defaults and the security values (7 day sessions, 10 sign-ins per 60 s, 12 character passwords, no two-factor requirement, passkeys and single sign-on off, 365 day personal keys) have no environment variable: they seed from code and are edited in Settings only. The session age, login limit, passkeys and single sign-on are read at boot (after restart in Settings); the password floor, the two-factor requirement and the key age apply at once.
Grants declared in mainframe.config.ts are desired state: every boot adds what is missing and removes a configured grant that left the file, and never touches a grant an administrator wrote in Settings → Access.
The shared environment helper supports _FILE variants for application secrets. To use Compose secrets, explicitly mount the file and pass its _FILE variable in the service configuration; the default deployment examples use a protected environment file.
Frontend map engine
Section titled “Frontend map engine”The engine is selected when Vite builds the frontend. The checked-in
apps/web/map-build.json selects Mapbox and supplies its public browser token,
so Pages and clean checkouts use the same defaults. Override these in the Pages
build environment or the ignored apps/web/.env.local file, then rebuild:
| Variable | Default | Purpose |
|---|---|---|
VITE_MAP_ENGINE |
mapbox |
maplibre or mapbox; other values fail the build |
VITE_MAPBOX_ACCESS_TOKEN |
map-build.json |
Public pk.* token; required for Mapbox builds |
VITE_MAP_STYLE |
unset | Optional style URL overriding the chosen engine’s defaults |
MapLibre keeps the registered PMTiles → Lithuania archive → CARTO resolution.
Mapbox starts with dimmed Satellite Streets imagery (65% maximum brightness,
slightly reduced saturation). Labels and operational overlays remain at full
brightness. Layers → Satellite / Map switches the basemap in Mapbox builds.
Map mode follows the application’s light/dark theme. An explicit
VITE_MAP_STYLE fixes the basemap and hides that selector.
Both engines use the same markings, selection, route, drone, camera-footprint, coverage, and detection code. The build selects the matching Terra Draw adapter for vertex editing and the matching GL markers for draggable points and circles. Style changes restore operational layers and the current edit draft. Mapbox uses flat classic styles to keep the existing overlay ordering and canvas coverage projection; custom styles must support these same GL layers and label fonts.
The configured pk.* token is public and included in the browser bundle.
Set VITE_MAP_ENGINE=maplibre to build the self-hosted MapLibre variant.
Engine changes require a new frontend build; backend environment changes
alone do not select an engine.
Voice capture and optional transcription
Section titled “Voice capture and optional transcription”Set VOICE_RECORDING=1 on Mainframe to enable automatic room mixes and original
speaker recording. Deploy the voice profile, provide private bucket access,
and retain the Egress backup volume during upload outages. VOICE_S3_ENDPOINT
can override the bucket address used inside Egress; it must identify the same
bucket as BLOB_S3_*. Historical audio remains readable with recording disabled.
Recording status and indexed coverage differ: token issuance is not proof of
audio capture. Monitor mainframe_voice_recordings,
mainframe_voice_recording_reconcile_failures_total and mainframe_comms_purge_pending.
Voice uses the Comms retention setting and honors archive preservation holds.
Transcription is deferred: its UI is hidden and no transcription backend runs,
even if VOICE_TRANSCRIBE_COMMAND or Whisper settings are present. The local
adapter code is retained for future use. Recording, playback and audible-activity
analysis do not require a speech model.