Skip to content

Configuration Reference

Every operator-specific value lives in configuration — an env file or a config file. Nothing is hardcoded. Copy each component's .env.example to .env to start.

Every service shares two behaviors:

  • Fail-fast validation. A typo'd value — a bad integer, an out-of-range port, a malformed key or URL — produces a startup error. The error names the exact variable; the service never falls back silently. If a service exits right after a config change, read its first log lines. Before you restart, dry-run the change with check-config mode. This mode prints the same problem list and exits 0 or 1, without booting anything. (The MCP's server_set_env tool runs this check for you before every restart.)

    docker compose run --rm panel node src/config.js --check
    
  • LOG_FORMAT=json. This opts in to structured logs: one {ts, level, svc, msg} JSON object per line, for log shippers like Loki, ELK, or CloudWatch. Leave it unset to keep the normal human-readable log lines.

Panel (panel/.env)

Key Default Description
DATA_DIR ./data Where the panel stores its Corestore and keys. Gitignored.
RELAY_ONLY false Route traffic through DHT relays to hide the panel's origin IP.
SERVICE_NAME Aliran Your service name. The app shows it while a viewer pairs with the service pairing code, before the viewer signs in. Public display text only.
ARGON2_MEM_KIB 262144 Argon2id memory cost, in KiB (about 256 MB).
ARGON2_TIME 3 Argon2id time cost, in iterations.
MAX_DEVICES_DEFAULT 2 Default number of concurrent devices allowed per user.
SESSION_TTL_DAYS 30 Session and token lifetime, in days. Long by design.
POW_DIFFICULTY 16 Login proof-of-work difficulty, in bits.
LOCKOUT_THRESHOLD 10 Failed login attempts allowed before lockout.
LOCKOUT_SECONDS 900 Lockout duration, in seconds.
LEGACY_PUBLISHER 1 Accept unnamed broadcaster registrations signed with the shared init publisher key. Set this to 0 once every broadcaster site is enrolled with add-publisher. This restricts registration to named, scoped identities only.
ADMIN_ENABLED false Serve the admin HTTP API from the panel process.
ADMIN_HOST 127.0.0.1 Admin API bind address. Put TLS in front if this isn't loopback.
ADMIN_PORT 3210 Admin API port.
ADMIN_SESSION_TTL_HOURS 12 Admin session token lifetime, in hours.
ESCROW_EXPORT 0 Serve POST /api/identity/escrow, which exports the panel keys encrypted. Off by default: it lowers identity theft from "shell access on the box" to "an admin session plus a password". The route answers 404 while this is off. The CLI export (admin-cli export-escrow) needs shell access, so it always works. See key escrow.
ESCROW_ARGON2_MEM_MIB / ESCROW_ARGON2_OPS 256 / 3 Argon2id cost for an exported escrow file. That file lives outside every control this box has, and the passphrase is its only protection — raise this if the box can take it. You pay the cost once per export, in a worker thread. The values travel inside each file, so an older file always opens with its own.
ANALYTICS_RETENTION_DAYS 90 How many days of aggregate-only analytics rollups the panel keeps, under DATA_DIR/analytics/. Set this to 0 to disable collection entirely — the panel writes no files, and its endpoints answer empty.
REPORTS_RETENTION_DAYS 30 How many days of viewer problem reports the panel keeps, under DATA_DIR/reports/. Set this to 0 to disable the feature entirely — the panel writes no files, and it stops serving the report RPC method.
REPORTS_MAX_PER_WINDOW / REPORTS_WINDOW_SECONDS 5 / 600 How many reports one reporter can send in the given window, in seconds.
REPORTS_ALERT_COUNT / REPORTS_ALERT_WINDOW_MIN 3 / 10 How many distinct reporters on one channel, within the given window in minutes, open a correlation alert.
REPORTS_STORM_SAMPLE 20 How many full report records the panel stores once an alert is open for a channel. Reports after that only increment a tally.
REPORTS_GLOBAL_PER_MIN 120 Panel-wide ingest limit. Past this rate, the panel acknowledges reports, counts them as shed, and drops them.
REPORTS_WEBHOOK_URL (empty) Webhook for ops notifications. One request body works for ntfy, Slack, and Discord. This is a secret — the credential lives in the URL path. Set it on the box by hand; never set it through the MCP. See recipes.
REPORTS_TELEGRAM_BOT_TOKEN / REPORTS_TELEGRAM_CHAT_ID (empty) Telegram bot target. Set both, or neither. The token is a secret.
SOURCES_SYNC_INTERVAL_MS 86400000 Default pull interval per source, in milliseconds (one day). Override per source with intervalMs.
SOURCES_TICK_MS 3600000 How often the scheduler checks which sources are due for a pull. This is a check, not a fetch.
SOURCES_BOOT_DELAY_MS 15000 Delay before the first due-check after the panel boots.
SOURCES_FETCH_TIMEOUT_MS 30000 HTTP timeout for one source pull.
SOURCES_MAX_BYTES 5242880 Maximum feed size the panel accepts. Enforced while streaming the download.
SOURCES_MAX_CHANNELS 500 Maximum entries the panel imports from one source. The panel truncates the feed beyond this.
SWARM_RCVBUF_MB / SWARM_SNDBUF_MB 2 / 2 Swarm UDP socket buffers, in MB (0 uses the OS default). Every client replicates the catalog over this one swarm. This only takes effect if the host allows it — the OS clamps it to net.core.{r,w}mem_max. The optional deploy/sysctl/install.sh script raises that ceiling. See the KB.
BOOTSTRAP (empty) Custom DHT bootstrap nodes. Optional.
BACKUP_DIR ./backups Where the dashboard's Backup page looks for recovery archives. The shipped compose file mounts the host ./backups here read-only and sets this to /backups — leave it unset there. The page can only list: a cold backup stops the service, so archives are made on the box with deploy/backup.sh.
CONFIG_SNAPSHOT_KEEP 20 How many on-box config snapshots the service keeps under DATA_DIR/config-snapshots/. It takes one by itself before a destructive change and before every restore or import; the oldest past the cap are removed.

Broadcaster (broadcaster/.env)

Key Default Description
DATA_DIR ./data Where the broadcaster stores its Corestore and drive keys.
PANEL_PUBKEY (required) The panel to register the stream with.
PUBLISHER_KEY (required) The publisher secret that signs register RPCs. Use a per-site key from the panel's add-publisher command — recommended when you run more than one broadcaster — or the shared legacy key from init.
PUBLISHER_NAME (empty) The enrolled publisher identity that matches PUBLISHER_KEY. When you set this, registrations verify against this site's own key and are limited to its admin-assigned channel scopes; the catalog also gets origin:<name> attribution. Leave it empty to use the legacy shared-key path.
STREAM_ID (optional) The catalog id for the legacy env-configured channel. In a multi-channel setup, add channels through the control API or UI instead.
INPUT (with STREAM_ID) One of: test, a file path, a pull URL (rtsp://, rtmp://, srt://, or http…m3u8), or a push listener type (rtmp, srt, or udp). The control API accepts these as typed objects.
RTMP_PORT 1935 When INPUT=rtmp, the port OBS pushes to.
PUBLIC_HOST (empty) The hostname shown in operator-facing push URLs (rtmp://<PUBLIC_HOST>:<port>/…).
INGEST_PORT_BASE / INGEST_PORT_MAX 5000 / 5999 The port range the broadcaster auto-allocates for push ingest. Each channel gets a validated, unique port in this range.
HLS_TIME 2 Segment duration, in seconds. Shorter means a faster time to first frame.
HLS_LIST_SIZE 8 Rolling playlist window, in segments. Use 12 (24 s at HLS_TIME=2) as the standard setting: it gives the viewer apps' ~10 s live offset a comfortable margin, and large swarms more blocks to share. Do not go below 8 — the viewer apps sit ~10 s behind the live edge.
FFMPEG_MAX_RSS_MB 150 Memory threshold, in MB, that recycles a running ffmpeg process. The broadcaster restarts ffmpeg once its VmRSS plus VmSwap crosses this. This bounds a slow memory buildup that some live-HLS upstreams cause in the demuxer. It uses the same watchdog backoff as a stalled-edge respawn, and does not rotate the feed. 0 disables this check. Linux only — it reads /proc.
FEED_BUFFER disk disk gives the feed a stable identity and a warm DHT topic, so joins are faster. ram keeps the disk byte-flat, but discovery starts cold on every restart. See the KB.
RESUME_PACE true Paces the boot auto-resume. Between channel starts, the broadcaster waits until its event loop catches up, so the control API and swarm stay responsive during a mass restart. Without this, recreating the whole fleet blacks out /api for minutes. The pace is adaptive: it barely waits on an idle box, and backs off on a loaded one. Set false for the old back-to-back resume.
RESUME_PACE_TARGET_MS 50 The event-loop lag, in ms, the pacer waits to fall below before starting the next channel. A higher value resumes faster, but leaves less API headroom during the resume.
RESUME_PACE_MAX_MS 1000 The cap on the per-channel wait. This keeps a permanently busy loop making forward progress. It bounds the total pacing overhead to roughly channels × this value.
RESUME_PACE_MIN 8 Below this channel count, the broadcaster does not pace at all — a handful of channels has no resume storm to manage.
RESUME_CONCURRENCY 12 How many channel starts run at once during boot. A channel start is mostly I/O wait, so running several at once cuts total recovery time roughly in proportion. Measured on 83 channels / 4 vCPU: 451 s sequential versus 40 s at concurrency 12, with a CPU peak of 77%. The adaptive pace still throttles the launch rate under load, so a high value self-limits on a constrained host. Set 1 for strictly sequential starts. Lower this value to keep the startup burst further from the host's CPU ceiling.
SLATE_ENABLED true Loops a pre-rendered "SOURCE OFFLINE" slate when a source is dead. This keeps the channel live with a message, instead of going blank during watchdog backoff. Set false to revert to the blank-during-backoff behavior. See the KB.
SLATE_DIR (image broadcaster/slate) Where the rendered slate .ts files live. The image builds these in at build time. Point this at the data volume instead — for example, /data/slate — to serve your own files.
SLATE_AFTER 3 Consecutive failed respawns, per configured source, before a channel gives up and shows the slate. A channel with fallback sources tries every URL first. A slated channel is remuxed with -c copy, which costs close to 0 CPU — cheaper than the live pull it replaces.
SLATE_RETRY_MS 30000 How often a slated channel drops the slate to re-probe its real source. This is also how the channel returns automatically: a working source clears the failure streak and stays; a still-dead source re-slates on the next failure. A lower value recovers faster. A higher value glitches the slate bars less often during a long outage, at the cost of the bars showing a little longer after the source is already back. Raise it — for example, to 60000 — for smoother bars. Values below about 20000 are discouraged. See the KB.
SWARM_MAX_PEERS (unset) An optional connection budget, per channel (each channel runs its own swarm; hyperswarm's own default is also 64, per channel). Connections beyond this budget are dropped at accept time. Leave headroom for non-viewer peers, such as repeaters and the panel's blobsKey probe.
SWARM_RCVBUF_MB / SWARM_SNDBUF_MB 2 / 2 Swarm UDP socket buffers, in MB (0 uses the OS default). UDX carries every peer stream over one socket pair, so this buffer is what overflows under fan-out — silently, as kernel packet drops. This only takes effect if the host allows itsetsockopt clamps it to net.core.{r,w}mem_max (212992 on stock Linux). The optional deploy/sysctl/install.sh script raises that ceiling. See the KB.
CONTROL_ENABLED false Serve the channel control HTTP API.
CONTROL_HOST 127.0.0.1 Control API bind address. Put TLS in front if this isn't loopback.
CONTROL_PORT 3310 Control API port.
CONTROL_SESSION_TTL_HOURS 12 Control session token lifetime, in hours.
LOCKOUT_THRESHOLD / LOCKOUT_SECONDS 10 / 900 Control login lockout threshold and duration.
ANALYTICS_RETENTION_DAYS 90 How many days of aggregate-only analytics rollups — per-channel peers, egress, and respawns — the broadcaster keeps, under DATA_DIR/analytics/. Set this to 0 to disable collection entirely.
ARGON2_MEM_KIB / ARGON2_TIME 65536 / 2 Argon2id cost for control-admin passwords.
BACKUP_DIR ./backups Where the dashboard's Backup page looks for recovery archives. The shipped compose file mounts the host ./backups here read-only and sets this to /backups — leave it unset there. The page can only list: a cold backup stops the service, so archives are made on the box with deploy/backup.sh.
CONFIG_SNAPSHOT_KEEP 20 How many on-box config snapshots the service keeps under DATA_DIR/config-snapshots/. It takes one by itself before a destructive change and before every restore or import; the oldest past the cap are removed.

Repeater (repeater/.env)

The repeater is a keyless regional relay appliance — see the repeater appliance page.

Variable Default Description
PANEL_PUBKEY — (required) The panel whose public catalog names the channels.
CHANNELS all all, an explicit list (ch1,ch2), or a category filter (category:news[,sports]).
RETENTION_SECONDS 300 The live window the repeater keeps per channel. This may exceed the origin's own HLS window.
SWARM_MAX_PEERS 256 Connection budget. A repeater exists to absorb fan-out, so this is set high.
SWARM_RCVBUF_MB / SWARM_SNDBUF_MB 4 / 4 Swarm UDP socket buffers, in MB (0 uses the OS default). This is higher than the broadcaster's default, because absorbing fan-out is this box's entire job — it is the most likely service to hit a buffer wall. Pair this with the optional deploy/sysctl/install.sh script; otherwise the OS silently clamps the request. See the KB.
DATA_DIR ./data Ciphertext block store. This is a disposable cache.
STATUS_INTERVAL_SECONDS 60 Per-channel status log cadence. 0 turns it off.
STATUS_PORT 0 (off) An opt-in health and metrics HTTP server: GET /healthz plus a Prometheus GET /metrics. Off by default — a stock repeater opens no listening sockets at all.
STATUS_HOST 127.0.0.1 Status-server bind address. Its endpoints are unauthenticated — widen this beyond loopback only on a network you control.
BOOTSTRAP public DHT Custom DHT bootstrap nodes.

Library (library/.env) — VOD

The library is the standalone VOD service — see the VOD library page and the reference.

Variable Default Description
DATA_DIR ./data The titles registry, per-title encryption keys (under secrets/, mode 0600), and the encrypted title stores. Disk use equals the sum of title sizes; only delete-title reclaims it.
PANEL_PUBKEY — (required to register) The panel to register titles with.
PUBLISHER_NAME / PUBLISHER_KEY The library's enrolled publisher identity. Always enroll the library as its own publisher, scoped to its title ids — for example, add-publisher library1 --scopes 'vod-*'. Never reuse the live fleet's key.
HLS_TIME 4 VOD segment length, in seconds. A shorter value gives finer seek and demand-paging, at the cost of more per-request overhead. Override this per title in the API.
INGEST_CONCURRENCY 1 Parallel ingest jobs. A transcode burst uses 0.5–1 core; the default runs jobs strictly one at a time.
SWARM_MAX_PEERS 256 Connection budget. One swarm carries every title — the same seeder economics as the repeater.
SWARM_RCVBUF_MB / SWARM_SNDBUF_MB 4 / 4 Swarm UDP socket buffers, in MB (0 uses the OS default). A seeder is send-dominant, so the same rationale — and the same sysctl pairing — applies as for the repeater.
CONTROL_ENABLED 0 The control API and a minimal UI on CONTROL_HOST:CONTROL_PORT (127.0.0.1:3320). This is the only way to add titles. Manage admins with library-cli add-admin.
CONTROL_SESSION_TTL_HOURS 12 Control session lifetime, in hours.
LOCKOUT_THRESHOLD / LOCKOUT_SECONDS 10 / 900 Control login rate limit.
ARGON2_MEM_KIB / ARGON2_TIME 65536 / 2 Control-admin password hashing cost.
BOOTSTRAP public DHT Custom DHT bootstrap nodes.
BACKUP_DIR ./backups Where the dashboard's Backup page looks for recovery archives. The shipped compose file mounts the host ./backups here read-only and sets this to /backups — leave it unset there. The page can only list: a cold backup stops the service, so archives are made on the box with deploy/backup.sh.
CONFIG_SNAPSHOT_KEEP 20 How many on-box config snapshots the service keeps under DATA_DIR/config-snapshots/. It takes one by itself before a destructive change and before every restore or import; the oldest past the cap are removed.

Client

The client reads build-time config from client/config, or a runtime service descriptor:

Key Description
panelPubKey The operator's panel public key. This pins trust and locates the service.
name / branding The app name, logo, and color palette.
bootstrap Custom DHT bootstrap nodes. Optional.

CDN channels are catalog config, not client config

To make a channel play a CDN/HLS link, add a per-channel redirect entry in the admin panel. Do not configure this on the client. See content-management.md.

Never commit secrets

Keys under DATA_DIR must never be committed. See .gitignore.