Skip to content

Settings

Three processes read settings files in TOML: the control plane (control.toml), the desk (desk.toml) and the chat (chat.toml, in The live chat). Secrets never go in a file; they come from the environment. weavesignal-control sample-config prints a complete control.toml with a fresh station identifier; the repository’s crates/desk/desk.example.toml is the desk’s, with a comment on every value. An unknown key in the control plane’s or the desk’s file is refused when the program starts, with the key named. The install routes write these files for you: the Compose bundle from its .env, the Cloudron package from its weavesignal.env.

Variable Read by Holds
WEAVESIGNAL_CONFIG, WEAVESIGNAL_DESK_CONFIG, WEAVESIGNAL_CHAT_CONFIG control plane, desk, chat each one’s settings file, if not --config (default control.toml, desk.toml, chat.toml)
WEAVESIGNAL_DATABASE_URL control plane, chat PostgreSQL’s address; over a Unix socket in the bundles (ADR-0092)
WEAVESIGNAL_LIVEKIT_API_SECRET control plane, desk LiveKit’s API secret, at least 32 characters
WEAVESIGNAL_CONTROL_ADMIN_TOKEN control plane, chat the station key: the station’s own bearer, with an admin’s powers; it makes the first staff account (Add staff and sign in). Unset, only staff sessions open the studio’s routes. The chat reads the studio’s view with it
WEAVESIGNAL_PLAYOUT_FEED_PASSWORD desk the password playout sends with its feed back to the desk ([video.playout_feed]); the bundles give it the listener source password
WEAVESIGNAL_CHAT_EMAIL_KEY chat service the secret that keys the hash of chatters’ email addresses and sign-in codes (ADR-0125): at least 32 characters, for example openssl rand -hex 32. Required: the chat exits at start, naming this variable, when it is missing or shorter (and Compose refuses to start without it). Never in the settings file: the Compose bundle keeps it in .env (mode 600), the Quadlets in a Podman secret. See The email key
WEAVESIGNAL_CONTROL_EMAIL_KEY control plane the secret that keys the hash of staff email addresses and their sign-in codes (ADR-0127): at least 32 characters, for example openssl rand -hex 32. Needed only when [mail] is on: the control plane then exits at start, naming this variable, when it is missing or shorter. Never in the settings file. A different key finds none of the addresses set under the old one, so keep it with the database’s backups. It is separate from the chat’s key, and the hashes are labelled apart, so one is never valid as the other. Every install route makes one: weavesignal.sh as CONTROL_EMAIL_KEY in the settings file, the Quadlets as a Podman secret, the Cloudron package in its secrets.env (Add staff and sign in)
WEAVESIGNAL_CONTROL_TOKEN_KEY control plane the secret that keys API tokens and webhook secrets (ADR-0195): at least 32 characters, for example openssl rand -hex 32. Every install route makes one (ADR-0202): weavesignal.sh and the Quadlets as CONTROL_TOKEN_KEY in the settings file, the Cloudron package in its secrets.env. Unset (a station installed before that), a key is derived from the station key (WEAVESIGNAL_CONTROL_ADMIN_TOKEN) and the control plane’s log says to set one; with neither, tokens and webhooks are off. A different key revokes every token and ends every webhook’s signatures, so rotating the station key does the same unless this is set. Never in the settings file; keep it with the database’s backups. See Integrations
RUST_LOG control plane, desk, chat overrides the log filter
Section Key Default Meaning
[station] id — the station’s identifier for its whole life (ADR-0047)
name — the station’s name
[listen] internal 127.0.0.1 the address internal services bind
[ports] thirteen keys see Ports every listening port (ADR-0075)
[room] public_url wss://station.example LiveKit as browsers reach it, through the proxy: an origin, to which LiveKit’s client adds /rtc
api_key weavesignal LiveKit’s API key
api_url http://127.0.0.1:7880 LiveKit’s server API on this machine, used to drop a caller
name studio the room’s name
desk_identity desk the desk’s identity prefix; nobody else may join under it
token_ttl_s 600 how long a room token can be used to join, 1 s to 4 h (ADR-0085)
[desk] socket none the desk’s control socket; left out, the studio app shows the desk as not set up
poll_ms 250 how often the desk is asked for its status, 50 to 5,000
[playout] socket, listeners_socket none playout’s control socket, and the listener process’s, through which the legal logger is turned on and off; left out, the logger cannot record
poll_ms 250 how often playout is asked for its state, 50 to 5,000
library_dir, library_lufs, library_max_seconds, library_max_upload_mb /var/lib/weavesignal/library, -23, 1800, 200 the music library’s files (playout reads the same directory), their loudness target, the longest track and the largest upload
[carts] media_dir /var/lib/weavesignal/carts the cart files the media worker writes (the desk’s carts.dir)
loudness_lufs, ceiling_dbtp -23, -1 the carts’ loudness target (−40 to −5) and the true peak no cart is raised above (−10 to 0)
max_upload_mb, max_seconds, worker_timeout_s 64, 600, 120 the largest upload, the longest cart, and how long the media worker may take over one file
worker, demo_pack none the media worker’s program (left out, this one), and a cart pack loaded when the station has no banks (Carts in the desk)
[retention] recordings_dir, sweep_every_s /var/lib/weavesignal/recordings, 3600 where recordings are kept (the sweeper deletes nothing outside it) and how often it runs, in seconds (60 or more: at most once a minute); the periods are the station’s setting, in the database (Set retention)
[studio] show empty the show on air, as the desk and the screener head it; the schedule does not set it (Set the show)
rundown none “coming up” rows; the schedule does not fill them (ADR-0102)
banned none callers banned before; the chat’s moderation log does not fill it (ADR-0102)
listener_delay_ms 15000 how far behind the studio listeners hear, not counting the profanity delay, which the studio adds (ADR-0103)
[publishing] public_url empty https:// and the station’s public host; every show card prints it without the scheme (ADR-0056), and the feeds, card images and episode files are reached under it; empty, cards and feeds are off
place empty where the station is, printed on the cards and written as podcast:location
zone Europe/London the station’s time zone as first offered: a new station starts with it and an admin confirms or changes it in Settings (ADR-0189); the zone in force is in the database
language en-gb the feeds’ language
owner_name, owner_email empty the feeds’ owner; without an address neither itunes:email nor podcast:locked is written
copyright, category, subcategory empty, Music, empty the feeds’ copyright line and Apple category
locked true podcast:locked: other platforms may not import the feeds
cards_dir /var/lib/weavesignal/show-cards the rendered cards, served at /cards/
episodes_dir /var/lib/weavesignal/episodes the published episode files, served at /media/episodes/
[video] (control plane) still_file, still_max_upload_mb, still_max_width, still_max_height /var/lib/weavesignal/carts/stills/slate.png, 24, 1280, 720 where the control plane writes the slate’s picture (the desk’s video.still_file), the largest upload, and the size it is fitted inside (The slate’s picture)
[video] (control plane) demo_still none a PNG that becomes the station’s picture when it has none, once: taken away, it stays away. The Compose bundle sets the demo station’s kettle (/opt/weavesignal/stills-demo/kettle-fm.png) through DEMO_STILL (ADR-0200)
[join] stream_url empty the listener stream as the player reaches it, through the proxy; empty, the player says the stream is not set up
call_in true whether the player offers call-in (ADR-0006)
consent_text, consent_contact the two-sentence words, empty what a caller agrees to before calling, and where they ask for a call to be taken down: an email address or a link, at most 200 characters. Call-in stays closed while the words name {contact} and it is empty (The consent words)
call_pass_ttl_s, invite_ttl_s 14400, 14400 how long a caller’s pass and an invite link last, 60 s to a day
calls_per_address_per_hour, calls_per_minute 6, 30 calls one address may place in an hour, and calls the whole station takes in a minute
forwarded_for false take the caller’s address from X-Forwarded-For’s last entry; set it only behind the station’s own proxy, or every caller shares the proxy’s address
hls_dir, hls_url /var/lib/weavesignal/hls, /hls where the desk writes its HLS, and the address the player fetches it from (an edge or a CDN’s, or the control plane’s own /hls/) (Video in the desk)
challenge_bits 18 call-in’s proof of work, in bits; 0 turns it off (The public pages)
challenge_bits_busy 21 the difficulty while the station’s minute of calls is at least half used
challenge_ttl_s 1800 how long a challenge can be answered, 60 s to a day
[web] dir none the public pages’ build (apps/web/dist; /opt/weavesignal/web in the server image), served at / with their caching and CSP; left out, the API only
listed false whether search engines may list the public address (the player, feeds and cards). Off, every response says X-Robots-Tag: noindex, nofollow, the pages carry a robots meta tag and /robots.txt disallows everything. On, only the API, the invite page and the echo test stay unlisted (The public pages)
[steering] ttl_s 300 how long a player keeps its steering manifest, in seconds; the specification recommends 300 (Content steering)
check_every_s, check_timeout_ms, down_after 5, 2000, 2 each edge’s health check, and how many failures in a row take it out of the order
clone_from . the pathway the multivariant playlist declares; edges with a host are sent as its clones
edges none the edge pool, [[steering.edges]] with pathway, host, weight, check_url; none, steering is off
[mail] mode, from, command, outbox_dir off, weavesignal@station.example, ["/usr/sbin/sendmail", "-t", "-i"], /var/lib/weavesignal/staff-outbox how staff sign-in codes reach staff (ADR-0127): command pipes each email to a sendmail-compatible program (msmtp, sendmail -t), outbox writes each to a file in outbox_dir (development; the files hold the addresses and the codes), off leaves staff signing in with a password. Needs WEAVESIGNAL_CONTROL_EMAIL_KEY
[api] public_per_minute, token_per_minute, streams_per_address 120, 600, 4 the public API’s limits (ADR-0195): requests per minute from one address to the open routes, requests per minute with one API token, now-playing streams one address may hold open; 0 turns one off (Integrations)
webhooks_allow_private false let webhooks point at private and loopback addresses (an integration on the station’s own network); off, a webhook must be https on a public address
[log] format json json (one object per line) or pretty
filter info the log filter

A rundown row and a ban look like this:

[[studio.rundown]]
at = "14:00" # HH:MM or HH:MM:SS, the station's time
title = "News"
detail = "hard post"
hard_post = true # the clock back-times to the first hard post ahead
key = "F2" # the cart key it needs, if any
[[studio.banned]]
identity = "tavish"
date = "2026-09-12"
reason = "Abuse on air"
Section Key Default Meaning
[livekit] url, api_key, room — the room, as the desk reaches it on the server
[desk] identity desk the desk’s identity prefix
jitter_buffer_ms 50 the receive jitter buffer; LiveKit’s 200 ms default doubles a guest’s delay
mix_buffer_ms 20 the mixing block: one pass computes every bus for a block this long; 10 or 20 (one Opus frame)
input_prebuffer_ms 50 the longest the mix waits for a late voice before mixing without it, 20 to 500
workers 0 threads encoding the returns; 0 is one per core, at most four
realtime true ask for real-time scheduling for the mix and the encoders, granted only where the host or container allows it (CAP_SYS_NICE or an rtprio limit)
return_opus_bitrate 128000 each person’s return, in bit/s
[programme] srt_bind, srt_port 127.0.0.1, 7313 where the desk listens for Liquidsoap; loopback only (ADR-0049, ADR-0086)
srt_latency_ms 20 SRT’s latency on loopback
opus_bitrate 96000 the programme’s Opus bitrate to playout
queue_ms 500 the leaky queues before the encoder and the SRT sink, so a playout that is away never stalls the desk
[programme.delay] target_ms, quiet_db, rebuild_slowdown 8000, -50.0, 0.02 the profanity delay toward playout, at most 30 s (0 turns it off); after a dump it builds back up through pauses quieter than quiet_db, and a few per cent slow otherwise
[programme.limiter] ceiling_db, lookahead_ms, release_ms -1.0, 1.5, 120.0 the limiter on the programme and on every mix-minus (ADR-0087)
[room] api_url empty LiveKit’s server API; empty, livekit.url with ws turned into http
poll_ms 500 how often the desk looks at the room
leave_grace_ms 30000 a person who left keeps their mix-minus this long
link_timeout_ms 8000 a subscription with no audio by then is made again (ADR-0089)
[control] socket none the control socket, such as /run/weavesignal/desk.sock; left out, it is off
[video] enabled, encoder, rungs, nvenc_sessions, camera_codecs, lag_ms, stale_ms, lower_third_ms, station, still_file false, auto, ["480p20"], 12, VP8 AV1 VP9, 100, 1000, 6000, WeaveSignal, /var/lib/weavesignal/carts/stills/slate.png the video path, off by default (ADR-0009); still_file is the slate’s picture, which the desk watches (The slate’s picture); [video.switch] the vision mixer’s rule, [video.hls] where and how the HLS is written, [video.room] the picture in the room (Video in the desk)
[video.playout_feed] enabled, bind, port, cushion_ms, crossfade_ms, off_air_hold_ms, absent_ms true, 127.0.0.1, 7317, 200, 300, 3000, 500 playout’s output back to the desk, the video’s sound while nobody is live (ADR-0172); loopback only; the password is in WEAVESIGNAL_PLAYOUT_FEED_PASSWORD (The sound)
[carts] dir, ahead_ms, start_wait_ms /var/lib/weavesignal/carts, 1000, 500 the cart files the desk plays (the control plane’s carts.media_dir), how far ahead each sounding cart is decoded, and how long a fire waits for its first audio (Carts in the desk)
[inputs.<role>] makeup_db and five stages (telephone, expander, compressor, leveller, limiter) presenter and DJ: their chain; caller: the telephone band (low_hz 300, high_hz 3400) and a compressor, while we run diagnostics (ADR-0194) each role’s input chain; an empty table turns it off

The presenter’s chain, in order:

Stage Default
expander (the gate) 1:3 below −34 dBFS, never more than 18 dB down
compressor 2.5:1 above −14 dBFS
leveller steers toward −25 dBFS of speech, within −6 to +18 dB, 0.5 dB a step
limiter −1 dBFS

The package writes every service’s settings itself on every start (control.toml, desk.toml, chat.toml, LiveKit’s and playout’s), from Cloudron’s environment and from two files the administrator can open in the File Manager: /app/data/weavesignal.env (settings) and /app/data/secrets.env (secrets, made on the first start). Edit those two, not the files the package writes. See Install on Cloudron.

Text and images CC BY-SA 4.0; code samples AGPL-3.0-only, as the code is. Licences.