Skip to main content

Configuration

The node is configured through TAVRN_* environment variables; this section explains how they are loaded and lists every variable by topic.

Every setting is a TAVRN_* environment variable; in the compose setup they come from .env (deploy/.env.example is the annotated template).

The node reads, later sources overriding earlier ones:

  1. the built-in defaults (the tables below),
  2. an optional TOML file named by TAVRN_CONFIG (keys are the variable names without TAVRN_ in lowercase, e.g. registration = "open"),
  3. the TAVRN_* environment variables.

Every value is then validated, and all problems are printed together (Configuration error: with one line per problem and the name of the variable); the process exits with code 2. Check a configuration without starting the server: docker compose exec tavrn tavrn-node check-config (prints the effective configuration as JSON; the passwords of the database, SMTP and Valkey URLs, the Turnstile secret, the GIF key, the OIDC client secret and the metrics token are never printed).

On/off switches accept true/false; those documented as on | off accept on/off as well as true/false. Lists are comma-separated. Sizes are in bytes.

--dev (or TAVRN_DEV=true) starts a development node with different defaults: domain localhost, listen 127.0.0.1:8080, SQLite in data-dev/ (database sqlite://data-dev/tavrn.db?mode=rwc), data directory data-dev, registration open, room creation local, log level debug,sqlx=info, no reference nodes, TAVRN_RTC_TCP_PORT=0, private application URLs allowed and 1000 legal notices per hour; http:// public URLs and OIDC issuers, TAVRN_FEDERATION_DEV_PEERS and TAVRN_DEV_MISBEHAVE are only accepted there. Never run --dev in production.

These settings fix the node’s identity domain, where it is served and which database it uses.

VariableDefaultMeaning
TAVRN_DOMAINrequiredIdentity domain of the node: addresses alice@<domain>, node keys, tokens. Lowercase, without scheme, path or port. Cannot be changed later without losing the node’s identity.
TAVRN_PUBLIC_URLhttps://<TAVRN_DOMAIN>Where the node (web client, API, /rtc) is served: https://<host>[:port], no path, query or fragment (http:// and IP addresses only with --dev). Everything that builds absolute URLs uses it (delegation).
TAVRN_NAMEtavrnDisplay name of the node (directory, discovery document).
TAVRN_DESCRIPTIONemptyShort description of the node (directory).
TAVRN_LISTEN0.0.0.0:8080Address and port of the plain-HTTP server.
TAVRN_TRUSTED_PROXIES127.0.0.0/8,::1/128IPs or CIDRs whose X-Forwarded-For is trusted. For connections from elsewhere the connection’s own address counts (rate limits, captcha), not the header. Also decides who may read /metrics without a token. List your reverse proxy here when it does not connect over loopback (another host, a container network); the compose file sets Docker’s private ranges, because there the proxy reaches the node through Docker’s gateway address. Never list networks that clients reach the node from directly: every address listed may choose its rate-limit identity.
TAVRN_DATABASE_URLempty (compose: built from POSTGRES_PASSWORD)postgres://user:password@host:port/db (or postgresql://). sqlite: only with --dev.
TAVRN_DATA_DIR/data (image)Directory for keys/, blobs/ (stored files) and the generated LiveKit configuration.
TAVRN_ADMIN_USERSemptyComma-separated usernames with node admin rights (besides the first registered account).
TAVRN_CONFIGunsetPath of an optional TOML configuration file (see above).

These settings decide who may create an account and how, which mail and captcha services take part, how passkeys and sessions work and the minimum age.

VariableDefaultMeaning
TAVRN_REGISTRATIONinviteopen, invite (account invites from the node administration) or closed. closed only blocks new accounts, never federation.
TAVRN_EMAILoptionalnone, optional or required (required needs TAVRN_SMTP_URL). With required an account signs in with its password only after confirming its address through the mailed link (<public URL>/verify-email?token=…, valid 24 hours); the device that registered stays signed in, and the login page offers to send the mail again.
TAVRN_SMTP_URLunsetsmtp://host:port (plain, internal relay), smtps://host:port (TLS) or smtp+starttls://host:port, optionally user:pass@. Test it with tavrn-node send-test-mail you@example.org.
TAVRN_SMTP_FROMnoreply@<TAVRN_DOMAIN>Sender address.
TAVRN_MAIL_TEMPLATES_DIR<TAVRN_DATA_DIR>/templates/mailYour own texts for the node’s mails, per language: <dir>/<language>/<name>.txt. See Mail languages and templates.
TAVRN_CAPTCHA_PROVIDERnonenone or turnstile (Cloudflare Turnstile).
TAVRN_TURNSTILE_SITE_KEY, TAVRN_TURNSTILE_SECRET_KEYunsetRequired with TAVRN_CAPTCHA_PROVIDER=turnstile.
TAVRN_CAPTCHA_REGISTERtrueRequire the captcha at registration (only with a provider).
TAVRN_CAPTCHA_LOGINfalseRequire the captcha at login (only with a provider).
TAVRN_WEBAUTHN_ORIGINthe public URLOrigin of the web client for passkeys. Override only for development and tests (e.g. http://localhost:5173).
TAVRN_WEBAUTHN_RP_IDTAVRN_DOMAINRelying party id of passkeys; must be the host of the origin or a parent domain of it, otherwise the node refuses to start. Changing it later invalidates every registered passkey (Passkeys).
TAVRN_WEBAUTHN_APP_ORIGINSemptyWebAuthn origins of your own apps besides the web origin, comma-separated: android:apk-key-hash:<base64url SHA-256 of the signing certificate> per Android signing key. Lets the Android app use passkeys in the app (Passkeys in the apps); published as webauthn.origins. Invalid entries stop the start.
TAVRN_SESSION_ACCESS_MINUTES60Access token lifetime (5–1440). A revocation (logout, password change or reset, device removal, an admin disabling the account or resetting its second factor, a node ban, account deletion) ends the access tokens and WebSockets of the affected devices at once (401 auth.session_revoked, close code 4001), and their voice and call connections; the node checks the session behind a token at most every 5 seconds per device and instance.
TAVRN_SESSION_REFRESH_DAYS90Sliding refresh token lifetime (1–3650); every refresh extends it. SSO sessions keep TAVRN_OIDC_SESSION_DAYS as their hard end, federated sessions their 7 days.
TAVRN_RATE_USER_PER_MINUTE1200 (0 in development mode)API requests per user and minute over every authenticated route and WebSocket connect (429 with Retry-After beyond); 0 switches the general limit off. Route limits (login, uploads, messages, …) apply on top.
TAVRN_RATE_LIMIT_IPV6_PREFIX64Per-address rate limits (login, registration, password reset, connect, directory, the emoji and sound proxy, …) count IPv6 clients by this prefix (32–128): one host usually holds a whole /64. IPv4 addresses count one by one.
TAVRN_PASSWORD_HASH_CONCURRENCY0 (twice the CPU cores, at least 2)Password hashes and checks (Argon2id, 64 MiB each) running at once, off the request threads; further logins wait for a slot.
TAVRN_WS_MAX_SOCKETS_PER_USER20WebSocket connections one user may hold at once per instance (0 = unlimited); beyond it the socket is closed with 4002 too_many_connections. App tokens keep their own limit of two.
TAVRN_DEVICE_STALE_DAYS180Devices unused this long are deleted by the hourly housekeeping (never a user’s only device, never one with a live session); 0–36500, 0 = never. An account has at most 100 devices (clients refuse a keys document with more): a login that would create the 101st is refused with 403 auth.device_limit until the user removes a device they no longer use (device settings of a signed-in device; logging out with “forget this device” removes it too) or the housekeeping deletes stale ones.
TAVRN_E2EE_CLAIMS_PER_TARGET_PER_HOUR60KeyPackage claims for one user per hour from one source (this node, or one other node) by requesters who share no room, DM, group DM or friendship with the user (1–10000); ten times that per user over all sources. Requesters who share one of these, and a user’s claims for their own devices, are not counted. Protects users from having their KeyPackages drained by claims in many names without locking out their contacts.
TAVRN_MINIMUM_AGE16Minimum age for registration (13–99). Every registration path asks for the date of birth and refuses younger people; see Age gates.
TAVRN_DM_POLICYeveryoneUpper bound of every user’s “Who can message me”: friends, everyone_shared (friends and shared rooms) or everyone (open is the old name). Users choose within it (their default is everyone_shared); non-friends land in the recipient’s message requests; blocks always apply.

The second factor (TOTP or passkeys) is mandatory for every account and cannot be switched off.

The desktop app signs in on the node’s own web client (app sessions of the connect flow, FEDERATION.md §8 step 3, capability connect.app): the user signs in or registers there with any method, including passkeys, single sign-on and the captcha, and the app picks up the session. A passkey used there also unlocks the app’s encryption keys: the page seals the passkey’s unlock secret to a key only the app holds, and the node keeps that sealed box (at most 1 KiB, never logged, handed out once) until the app picks it up. There is nothing to configure; /connect must be served like every other page of the web client.

The node’s mails (email confirmation, password reset, account deleted and restored, takedown notices and statements of reasons, app token expiry) are written in the recipient’s language: the language the user picked in the client (the client stores it on the node; a new account starts with the browser’s language), for reporters of takedown notices the browser language of the notice form. The node has English and German texts; any other language falls back to English.

The texts are the templates in templates/mail/<language>/<name>.txt of this repository:

Subject: Reset your password
Hello {name},
you can set a new password via this link:
{link}

The first line is the subject (the node puts its name in front), the second is empty, the rest is the plain-text body. {name}-style variables are filled in; every template knows {node} (TAVRN_NAME), {domain} and {public_url} plus its own (check-config names them when one is wrong):

TemplateSent whenOwn variables
verify_emailan account registers with an e-mail address, or asks for the confirmation mail again (“Send the confirmation mail again” on the login page: a new link, at most three per account and hour){name}, {link}
password_reseta user asks for a password reset{name}, {link}
account_deleted, account_deleted_by_adminthe user or a node admin deleted the account{address}, {purge_after}
account_restoreda node admin restored a deleted account{address}
app_token_expiring_week, app_token_expiring_dayan application token expires in seven days or in one day{name}, {application}, {expires_at}, {link}
notice_received, notice_forwarded_ina takedown notice arrived (from the form or forwarded by another node){case}, {link} or {origin}
notice_content_removed, notice_account_disabled, notice_rejected, notice_forwardedthe decision on a notice, for its reporter{case}, {reason} or {nodes}
statement_content_removed, statement_account_disabledthe statement of reasons for the uploader{case}, {basis}, {statement}, {deadline}
counter_notice_received, counter_restored, counter_uphelda counter-notice and the decision on it{case}, {statement} and {signature} or {reason}

The language is chosen per recipient: a user whose language is de-AT gets de-at if there is such a template, otherwise de, otherwise English. To change a text, copy the file to TAVRN_MAIL_TEMPLATES_DIR (default /data/templates/mail) under the same language and name, for example /data/templates/mail/de/password_reset.txt, and edit it; you can also add a language the node has no texts for (/data/templates/mail/fr/…, used for users whose language is fr or fr-CA). The files are read when a mail is sent, so changes apply without a restart. tavrn-node check-config validates the directory (unknown names, a missing subject line, unknown variables, files over 64 KiB); a file that is broken when a mail goes out is skipped with a warning and the built-in text is sent instead.

With an OpenID Connect provider configured, people sign in with the provider’s accounts, and its groups can be mapped to roles.

VariableDefaultMeaning
TAVRN_OIDC_ISSUERunsetIssuer URL of the OpenID provider; SSO is on when set. https:// only (http:// with --dev), must match the provider’s issuer exactly.
TAVRN_OIDC_CLIENT_IDunsetClient id at the provider (required with an issuer).
TAVRN_OIDC_CLIENT_SECRETunsetClient secret of a confidential client (never printed). Use this or the file variant.
TAVRN_OIDC_CLIENT_SECRET_FILEunsetFile holding the client secret (keeps it out of the environment), e.g. /data/keys/oidc-client-secret.
TAVRN_OIDC_SCOPESopenid profile email groupsRequested scopes (space or comma separated); must contain openid.
TAVRN_OIDC_USERNAME_CLAIMpreferred_usernameClaim the username of new accounts comes from (sanitized, -2 appended on a collision).
TAVRN_OIDC_DISPLAY_NAME_CLAIMnameClaim the display name comes from (updated on every login).
TAVRN_OIDC_GROUPS_CLAIMgroupsClaim holding the groups (array of strings), from the ID token or UserInfo.
TAVRN_OIDC_BUTTON_LABELemptyText of the login button (at most 64 characters); empty: the client’s “Sign in with SSO”.
TAVRN_OIDC_ONLYfalsetrue: password and passkey logins and registration only for TAVRN_ADMIN_USERS (break-glass accounts).
TAVRN_OIDC_AUTO_PROVISIONtrueCreate accounts on the first SSO login.
TAVRN_OIDC_GROUP_MAPunsetJSON group → rights map, e.g. {"it-admins":{"node_admin":true},"staff":{"room_creation":5}}; when set, the admin API cannot change it.
TAVRN_OIDC_GROUP_SYNCaddadd grants only; sync also revokes what an earlier sync granted (never manual grants).
TAVRN_OIDC_SESSION_DAYS7Hard end of an SSO session in days (1–30).

Setting it up with a provider (protocol: APPLICATIONS.md §8):

  1. Create a confidential OAuth2/OpenID client at the provider with the authorization code flow and PKCE (S256) and register exactly this redirect URI: <TAVRN_PUBLIC_URL>/api/v1/auth/oidc/callback (by default https://<TAVRN_DOMAIN>/api/v1/auth/oidc/callback). As post-logout redirect URI register <TAVRN_PUBLIC_URL>/login. Sign ID tokens with RS256, PS256, ES256 or EdDSA (never HS256). Add a groups claim if you want the group mapping.

    • Keycloak: client type OpenID Connect, client authentication on, standard flow only, “PKCE method” S256. For groups add a “Group Membership” mapper (token claim name groups, “Full group path” off). Issuer: https://keycloak.example/realms/<realm>.
    • Authentik: provider type “OAuth2/OpenID Provider”, client type confidential, signing key set (RS256). The default scope mappings include groups. Issuer: https://authentik.example/application/o/<slug>/ (with the trailing slash, exactly as shown in the provider’s overview).
  2. Configure the node (secrets preferably through a file):

    Terminal window
    TAVRN_OIDC_ISSUER=https://keycloak.example/realms/company
    TAVRN_OIDC_CLIENT_ID=tavrn
    TAVRN_OIDC_CLIENT_SECRET_FILE=/data/keys/oidc-client-secret
    # optional:
    TAVRN_OIDC_BUTTON_LABEL=Sign in with Company SSO
    TAVRN_OIDC_GROUP_MAP={"it-admins":{"node_admin":true},"staff":{"room_creation":5},"devs":{"rooms":[{"room":"01J…","roles":["01J…"]}]}}
    TAVRN_OIDC_GROUP_SYNC=add # sync also revokes what an earlier sync granted (never manual grants)
    TAVRN_OIDC_ONLY=false # true: password logins only for TAVRN_ADMIN_USERS (break-glass)
  3. Check it: docker compose exec tavrn tavrn-node check-config fetches the provider’s discovery document and keys and prints the redirect URI to register.

Notes: provider requests use verified TLS, follow no redirects and may target LAN addresses. An unreachable provider never stops the node (it is contacted on demand). SSO logins need no tavrn second factor (the provider is responsible), but end-to-end encryption still asks for the recovery key on the first login. Existing password accounts link their provider identity themselves in Settings → Security (the node never links by e-mail or username). Without TAVRN_OIDC_GROUP_MAP admins edit the mapping through PUT /api/v1/admin/sso/group-map; group changes take effect at the next SSO login, at the latest after TAVRN_OIDC_SESSION_DAYS. With TAVRN_OIDC_ONLY=true keep at least one account in TAVRN_ADMIN_USERS, so an outage of the provider cannot lock you out; those names are never given to accounts created by SSO. TAVRN_OIDC_CLIENT_ID, TAVRN_OIDC_ONLY or TAVRN_OIDC_GROUP_MAP without an issuer is a configuration error.

These settings limit who creates rooms and how many, how large uploads may be and how stored files are kept.

VariableDefaultMeaning
TAVRN_ROOM_CREATIONinviteWho may create rooms: none (node admins only), invite (node admins and users with a room-creation grant), local (every local user) or federated (also users of other nodes signed in through federation).
TAVRN_MAX_ROOMS_PER_USER5Rooms a user may own (entitlement rooms.max_owned; 0–1000000, 0 = unlimited).
TAVRN_UPLOADS_ENABLEDtruefalse switches file uploads off.
TAVRN_UPLOAD_MAX_BYTES26214400 (25 MiB)Largest upload (entitlement upload.max_bytes); must be > 0 while uploads are on. The proxy must allow bodies at least this large.
TAVRN_UPLOAD_ALLOWED_TYPESimage/*,video/*,audio/*,application/pdf,text/plain,application/zipMIME types allowed for attachments in unencrypted conversations (the declared type; wildcards like image/* allowed; others get 415 upload.type_not_allowed). Encrypted uploads and files channels are limited by the upload categories only (Who may upload what).
TAVRN_UPLOAD_ALLOW_EXECUTABLESfalseExecutables, scripts, installers and disk images (exe, msi, bat, sh, jar, apk, dmg, iso, …; PROTOCOL.md §7a) are refused unless this is true and the member’s upload categories (role setting) include other.
TAVRN_UPLOAD_DM_CATEGORIESimages,audio,video,documents,archives,otherUpload categories in DMs and group DMs hosted here (rooms set them per role and channel).
TAVRN_STORAGE_QUOTA_PER_ROOM_BYTES10737418240 (10 GiB)Storage per room (entitlement storage.max_bytes_per_room).
TAVRN_STORAGE_ENCRYPTIONonEncryption at rest of every stored file; off stores new files unencrypted (debugging only; encrypted files stay readable). See Storage and media.
TAVRN_RETENTION_DAYS0Message retention: messages of channels, DMs and group DMs hosted here are deleted this many days after they were sent by the hourly housekeeping, with their attachments, reactions and pins. Pinned messages and the welcome channel’s entries are kept. Rooms may only shorten it (room setting). 0–36500, 0 = keep forever. See Message retention.
TAVRN_EMOJI_MAX_PER_ROOM100Custom emojis per room (entitlement emoji.max_per_room).
TAVRN_EMOJI_MAX_BYTES262144 (256 KiB)Largest custom emoji file.
TAVRN_PREVIEW_AUTO_MAX_BYTES5242880 (5 MiB)Images up to this size load as previews by themselves where the user’s setting allows it (always / friends and shared rooms / never); larger ones show a placeholder with a “Load” button. 0 = never automatically.
TAVRN_MEDIA_CACHE_UNUSED_DAYS365Cached copies of other nodes’ custom emojis and soundboard clips are evicted after this many days without use; 0 = never.
TAVRN_MEDIA_CACHE_MAX_BYTES2147483648 (2 GiB)Total size of those cached copies; beyond it the least recently used go (checked at most every 10 seconds per instance and by the hourly housekeeping).
TAVRN_MEDIA_CACHE_MAX_BYTES_PER_HOST67108864 (64 MiB)Size of the cached copies from one origin node (at most TAVRN_MEDIA_CACHE_MAX_BYTES); beyond it that node’s least recently used copies go. Fetches for copies not cached yet are limited to 60 per client address and minute.

Entitlements (protocol: ENTITLEMENTS.md) are the per-user and per-room limits. The values here and the ones marked “entitlement” above are the node defaults; node admins extend them per user or room with plans and grants (admin page “Entitlements” or the admin API with a tavrn_adm_ token). 0 = unlimited unless noted.

VariableDefaultMeaning
TAVRN_UPLOAD_MAX_BYTES_CEILING0Largest upload a grant can allow; 0 = TAVRN_UPLOAD_MAX_BYTES (grants cannot raise it). Otherwise at least TAVRN_UPLOAD_MAX_BYTES; also the body limit of the upload routes.
TAVRN_VOICE_CHANNEL_MAX_PARTICIPANTS0voice.max_participants per voice channel (0–1000; 0 = only TAVRN_VOICE_MAX_PARTICIPANTS).
TAVRN_STREAM_MAX_HEIGHT0stream.max_height, screen share height in pixels (0–4320; 0 = only TAVRN_SCREENSHARE_MAX_RESOLUTION). Enforced by the clients.
TAVRN_STREAM_MAX_FPS0stream.max_fps (0–240; 0 = only TAVRN_SCREENSHARE_MAX_FPS). Enforced by the clients.
TAVRN_CAMERA_MAX_HEIGHT0camera.max_height in pixels (0–4320; 0 = only TAVRN_CAMERA_MAX_RESOLUTION). Enforced by the clients.
TAVRN_EMOJI_MAX_PERSONAL20emoji.max_personal, personal emojis per user (0–100000).
TAVRN_SOUNDS_MAX_PER_ROOM50sounds.max_per_room, soundboard clips per room (0–100000).
TAVRN_SOUNDS_MAX_PERSONAL10sounds.max_personal, personal soundboard clips (0–100000).
TAVRN_ATTACHMENT_RETENTION_DAYS0attachments.retention_days: attachments of channels and DMs are deleted this many days after the upload by the hourly housekeeping; messages keep listing them as “file expired”. Pinned messages keep their files. Rooms may only shorten it. 0–36500, 0 = keep forever.
TAVRN_FILES_CHANNELSonfiles_channels.enabled: files channels may be created (on/off).
TAVRN_FILES_CHANNELS_MAX_BYTES10737418240 (10 GiB)files_channels.max_bytes, storage per files channel on the node’s own storage (never external stores, which have their own limit in the channel’s storage settings). Every member with Attach files can fill it, and a room may have several files channels: size it to the disk. 0 = unlimited (only where the disk is guarded otherwise); grants and plans can raise it per room.
TAVRN_FILES_EXTERNALoffFiles channels on external WebDAV/S3 stores (protocol: FILES.md §5).
TAVRN_FILES_INTERNALonfiles_channels.internal: off = files channels on external stores only; the node keeps no files of files channels (needs TAVRN_FILES_EXTERNAL=on, see Files channels on external stores). A plan or override of the room owner (or a room grant) with files_channels.internal 1 allows the node’s own storage in that owner’s rooms anyway, 0 takes it away on a node that is on.
TAVRN_FILES_EXTERNAL_PRIVATEoffon also allows external stores on private, loopback or link-local addresses and plain http (stores in the LAN); this disables the SSRF protection for them.

These settings control the conversion of images and videos, GIF search, link previews and the node’s search in plaintext channels.

VariableDefaultMeaning
TAVRN_MEDIA_FFMPEGautoMedia pipeline for avatars, profile headers, room icons and video backgrounds: auto (ffmpeg from PATH; the image ships one), off (only static PNG/JPEG/WebP, no animations or video input) or an absolute path to ffmpeg.
TAVRN_MEDIA_WORKERS1Concurrent conversions (1–8); a few more uploads wait, the rest get 503 media.busy.
TAVRN_WHITEBOARD_MAX_SCENES50Whiteboard scenes per user in the encrypted library (1–1000, at most 5 MB each).
TAVRN_GIF_PROVIDERnoneGIF search: none, giphy or tenor. The API key stays on the node; clients only ask the node. Answers are cached (trending 5 minutes, searches 10 minutes) and each user may search 30 times a minute, so a few users cannot use up the key’s quota; a provider error answers 503 gif.unavailable.
TAVRN_GIF_API_KEYunsetRequired with a GIF provider.
TAVRN_UNFURLonLink previews: the sender’s client asks this node to load a linked page (public addresses only, 5 s, 1 MiB of HTML, image downscaled to a 40 KiB WebP) and puts the preview into the message. The node sees the URLs its users preview; readers never fetch anything. off: no previews from this node (404 unfurl.disabled).
TAVRN_SEARCH_ENABLEDtrueNode-side message search over unencrypted channels (protocol: SEARCH.md); false removes the route (404 search.disabled) and stops indexing. Encrypted conversations are only searched in the clients’ local encrypted index.
TAVRN_SEARCH_RATE_PER_MINUTE30Search requests per user and minute (1–10000).

The node starts and supervises livekit-server with a configuration generated from these values (Voice and video explains ports and addresses).

VariableDefaultMeaning
TAVRN_VOICE_ENABLEDtruefalse: no voice channels, no LiveKit process.
TAVRN_VOICE_MAX_BITRATE_KBPS192Audio bitrate cap for every voice channel (8–512). New channels start at 64 kbps; owners pick a quality preset up to the cap (Saver 8, Standard 32, High 64, Studio 128, Music 192 stereo). Calls in DMs use 64 kbps, or the cap when it is lower. Lower it to save bandwidth: the clients show the presets above it as limited by this node.
TAVRN_VOICE_MAX_PARTICIPANTS50Participants per voice channel at most (1–1000); channel user limits apply below it.
TAVRN_VOICE_RECONNECT_GRACE_SECS45A dropped participant stays “reconnecting” this long (0–600; 0 = leave at once).
TAVRN_SCREENSHARE_ENABLEDtrueScreen sharing.
TAVRN_SCREENSHARE_MAX_RESOLUTION1080p720p, 1080p, 1440p or 4k.
TAVRN_SCREENSHARE_MAX_FPS301–60.
TAVRN_CAMERA_ENABLEDtrueWebcam sharing (the use_camera permission still applies).
TAVRN_CAMERA_MAX_RESOLUTION720p360p, 720p or 1080p.
TAVRN_CAMERA_MAX_FPS301–60.
TAVRN_RTC_UDP_PORT_RANGE61000-61100UDP media ports (from-to, from < to, both > 1024), published 1:1. Keep it outside the Linux ephemeral range 32768–60999.
TAVRN_RTC_EXTERNAL_IPunsetPublic address LiveKit announces (IPv4, or a hostname resolved at start and every minute). Empty together with TAVRN_RTC_ADVERTISE_IPS: detected via STUN (IPv4 and IPv6).
TAVRN_RTC_ADVERTISE_IPSemptyMore addresses, comma-separated (IPs or hostnames): the first IPv4 and the first IPv6 (with TAVRN_RTC_EXTERNAL_IP) replace the container’s private addresses, every further one is handed to clients as an extra candidate. No link-local, multicast or 0.0.0.0 addresses.
TAVRN_RTC_ADVERTISE_PRIVATEfalsetrue: private addresses (LAN, ULA) that hostnames resolve to are announced too (IP literals are always taken as configured).
TAVRN_RTC_STUN_SERVERSempty (LiveKit’s defaults)host:port list for the address detection.
TAVRN_RTC_TCP_PORT7881ICE over TCP, the fallback when UDP is blocked; publish and forward it like the UDP range. 0 = off, otherwise 1025–65535 and different from the other TCP ports.
TAVRN_RTC_TURN_ENABLEDfalseLiveKit’s embedded TURN server for clients behind strict NATs or firewalls. Needs at least one of the two TURN ports.
TAVRN_RTC_TURN_UDP_PORT3478TURN over UDP, published and forwarded (0 = off); must not lie in the media range.
TAVRN_RTC_TURN_TLS_PORT5349Plain TCP port behind the proxy’s TLS termination for TURN over TLS on 443 (0 = off).
TAVRN_RTC_TURN_DOMAINturn.<host of TAVRN_PUBLIC_URL>Name clients use for turns:<domain>:443 (turn.<TAVRN_DOMAIN> when the public URL’s host is an IP address).
TAVRN_RTC_TURN_RELAY_PORT_RANGE30000-30999Relay sockets of the TURN server inside the container (not published); must not overlap the media range.
TAVRN_LIVEKIT_PORT7880LiveKit’s signaling/API port on 127.0.0.1 inside the container (1025–65535, not the TAVRN_LISTEN port). Never published; clients use /rtc.
TAVRN_LIVEKIT_BINARYlivekit-serverPath or name in PATH of the LiveKit binary (outside Docker: just livekit-install).

These settings decide whether and with whom the node federates and how it takes part in the node directory.

VariableDefaultMeaning
TAVRN_FEDERATION_ENABLEDtruefalse: no federation tokens, federated logins, inbox or outbox.
TAVRN_REFERENCE_NODESempty (.env.example: tavrn.org, the official node)Comma-separated domains of the reference nodes this node announces itself to and pulls the node list from (directory). Lowercase, at least one dot, no port.
TAVRN_DIRECTORY_LISTINGtruefalse: never announce; other nodes refuse to list this node.
TAVRN_DIRECTORY_MAX_ROOMS5000Cached rooms of other nodes for Discover in total (at most 200 per node; > 0).
TAVRN_FEDERATION_BLOCKLISTemptyDomains never federated with (also blocks their subdomains).
TAVRN_FEDERATION_ALLOWLISTemptyIf set, federate only with these domains (the blocklist still wins).
TAVRN_FEDERATION_ALLOW_PRIVATEfalsetrue allows peers on private or loopback addresses (development, tests, closed intranets).
TAVRN_FEDERATION_SRV_FALLBACKtrueWhen a peer’s identity domain serves no /.well-known/tavrn (404, or no HTTPS server at all), look up _tavrn._tcp.<domain> SRV and fetch the document from its target (SRV record).
TAVRN_FEDERATION_REMOTE_BOTStrueAccept bots of other nodes.
TAVRN_FEDERATION_RATE_LOGINS60Federated logins per remote domain and minute (> 0). One account, and one client address on the network (not loopback), get a quarter of it (at least 1).
TAVRN_FEDERATION_RATE_INBOX300Inbox messages per remote domain and minute (> 0).
TAVRN_FEDERATION_RATE_API3000API requests of all users of one remote domain per minute (> 0).
TAVRN_FEDERATION_RATE_API_PER_USER600API requests of one remote user per minute (> 0), counted before the domain’s budget and at most that budget: requests it refuses do not use up the domain’s budget, so one account cannot lock the other users of its domain out. Keep it well below TAVRN_FEDERATION_RATE_API.
TAVRN_FEDERATION_RATE_CLAIMS120KeyPackage claims per requesting node and minute (> 0).
TAVRN_FEDERATION_MAX_REMOTE_USERS10000Remote users in total. Users that only an incoming message names (the sender of a friend request) may fill it up to nine tenths; the rest stays for users who sign in.
TAVRN_FEDERATION_MAX_REMOTE_USERS_PER_DOMAIN1000Remote users per home domain.
TAVRN_FEDERATION_PERMIT_TIMEOUT_SECS10How long a request to a peer waits for one of the peer’s 4 connection slots and then one of the 64 node-wide slots before it fails (1–120). A slow peer only ever ties up its own slots.
TAVRN_FEDERATION_MAX_UNVERIFIED_PEERS1000Cached discovery documents of peers no signed request of which ever verified here (10–1000000). Beyond it, unsigned requests naming a new domain are refused (503 federation.peer_limit) before any fetch. Peers this node looked up itself, listed in its directory, or with users or sign-ins never count. Failed fetches are remembered for 60 seconds per domain, and peers unused for 30 days that nothing ties to the node are pruned.
TAVRN_FEDERATION_DISCOVERY_SLOTS12Discovery fetches at the same time for unknown issuers of requests not verified yet (1–64). When all are taken such requests are refused at once (503 federation.busy), so unsigned requests naming many slow domains cannot take the connection slots that known peers need.
TAVRN_FEDERATION_REMOTE_USER_RETENTION_DAYS90Remote users without a federated login for this many days and with nothing left on this node (no memberships, friendships, blocks, messages, …) are deleted by the hourly housekeeping; 0 keeps them forever.
TAVRN_FEDERATION_NOTIFY_BANSfalsetrue: tell the home node of a remote user that a room or node ban removed them from a room (ban.notify, information only, never the reason).
TAVRN_FEDERATION_REQUIRE_CONFORMANCEfalsetrue: also refuse federated logins and inbox messages of nodes that were not checked yet or failed (conformance checks).
TAVRN_FEDERATION_DEV_PEERSemptyDevelopment only (refused without --dev): domain=http://host:port pairs for local multi-node setups.
TAVRN_DEV_MISBEHAVEemptyDevelopment only (refused without --dev): a node that breaks the protocol on purpose for conformance tests (accept_bad_signatures, replay_ok, plaintext_ok, no_rate_limit). Never in production.

Bot tokens are a single factor: rooms, categories and channels can be declared “no bots” in their settings (protocol: APPLICATIONS.md §1.2, §9.6). Details: Webhooks and applications.

VariableDefaultMeaning
TAVRN_APP_CREATIONallWho may create applications: all (every local user with a full session), invite (users with an application grant from an admin) or admins.
TAVRN_APPS_PER_USER5Applications per owner (0–100); node admins are not exempt.
TAVRN_APPS_PER_ROOM25Installed applications per room (0–1000).
TAVRN_APP_TOKEN_MAX_DAYS365Maximum app token lifetime (1–3650 days).
TAVRN_APP_RATE_REQUESTS1200API requests (REST and WebSocket operations) per application and minute (> 0).
TAVRN_APPS_FEDERATEtrueThis node’s bots may obtain federation tokens for other nodes.
TAVRN_APP_ALLOW_PRIVATE_URLSfalse (--dev: true)Application URLs (interactions URL, event subscriptions) on private or loopback addresses (intranets; turns the SSRF protection off for them).
TAVRN_APP_COMMANDS_REQUIRE_APPROVALfalseNode admins approve every application command definition before it is offered in this node’s rooms (a changed definition is pending again).
TAVRN_COMPONENT_MAX_AGE_DAYS30Buttons and select menus of application messages older than this many days answer 410 component.expired (0 = no limit, at most 3650).
TAVRN_WEBHOOKS_ENABLEDtrueIncoming webhooks; false refuses creation and execution (webhook.disabled).
TAVRN_WEBHOOKS_PER_CHANNEL10Webhooks per channel (1–50; a room has at most 50).

These settings tune the flood detection that works in every channel, encrypted or not.

VariableDefaultMeaning
TAVRN_AUTOMOD_BURST_LIMIT10More than this many messages of a user per channel within 5 seconds count as spam (0 = off).
TAVRN_AUTOMOD_TIMEOUT_MINUTES10Timeout in minutes after a spam burst.

Reports and content auto-moderation add TAVRN_REPORTS_RETENTION_DAYS, TAVRN_REPORTS_RATE_PER_HOUR, TAVRN_REPORTS_INBOUND_PER_HOUR, TAVRN_AUTOMOD_WORDS_FILE and TAVRN_AUTOMOD_LINKS; see Moderation and auto-moderation.

Details: Legal pages and GDPR.

VariableDefaultMeaning
TAVRN_LEGAL_NOTICESoffon opens the public takedown notice form /legal/notice (captcha when configured). Notices forwarded by other nodes are accepted either way.
TAVRN_LEGAL_NOTICE_RATE_PER_HOUR5 (--dev: 1000)Notices per client address and hour (1–1000).
TAVRN_LEGAL_NOTICE_RETENTION_DAYS1095Cases are kept this long after their last change (30–36500; 1095 = 3 years).
TAVRN_LEGAL_NOTICE_INBOUND_PER_HOUR10Notices forwarded by other nodes accepted per sending registrable domain (its subdomains share the budget) and hour (1–1000); five times as many from all domains together. Only nodes this node has history with (its users signed in there, users of that node known here for at least a day, or listed in its directory) may forward; forwarded cases get no confirmation mail here.
TAVRN_LEGAL_REPEAT_THRESHOLD3Actioned notices against one user after which the admin page suggests disabling the account (1–100; a suggestion only, never automatic).
TAVRN_LEGAL_COUNTER_NOTICE_DAYS14Days the uploader may answer an actioned notice with a counter-notice (1–365).
TAVRN_LEGAL_TERMS_MODEnoticenotice: ask once after a new version of the terms; require: refuse the API (403 legal.terms_required) until accepted (declining signs out).
TAVRN_ACCOUNT_DELETE_GRACE_DAYS30Deleted accounts can be restored by an admin this long, with the username reserved; then they are erased (1–365).

These settings switch web push notifications and the Prometheus metrics endpoint on or off and configure them.

VariableDefaultMeaning
TAVRN_PUSHonWeb push notifications to browsers and installed web apps (PWAs). See Web push.
TAVRN_PUSH_CONTACTthe node’s public URL (TAVRN_PUBLIC_URL)VAPID contact, a mailto: address or an https:// URL (at most 256 characters) that push services may use to reach the operator.
TAVRN_PUSH_MAX_PER_MINUTE10Pushes per user and minute (1–1000); in addition at most one push per conversation and user every 30 seconds.
TAVRN_METRICSoffon exposes GET /metrics (Prometheus text format) on the node’s HTTP port. See Metrics and health.
TAVRN_METRICS_TOKENemptyWhen set (16–256 printable ASCII characters without spaces), scrapers must send Authorization: Bearer <token>; never printed by check-config. When empty, /metrics answers only to loopback and the addresses in TAVRN_TRUSTED_PROXIES.

Details: Scaling with Valkey.

VariableDefaultMeaning
TAVRN_CACHE_URLunsetredis://[user:password@]host[:port][/db] or rediss://… (TLS); enables several instances. The password never appears in logs or check-config; URL-encode special characters.
TAVRN_INSTANCE_IDrandom per start (logged)[A-Za-z0-9._-]{1,64}; names the instance in logs, the cache and livekit-<id>.yaml.
TAVRN_INSTANCE_URLaddress towards the cache + listen porthttp(s)://host[:port] where the other instances reach this one.

These settings control how much the node logs, in which format, and which slow requests and statements it reports.

VariableDefaultMeaning
TAVRN_LOG_LEVELinfo (--dev: debug,sqlx=info)A log filter: a level (error, warn, info, debug, trace) or per-module directives such as info,sqlx=warn. An invalid filter falls back to info.
TAVRN_LOG_FORMATtexttext or json (one JSON object per line, for log collectors).
TAVRN_LOG_SLOW_REQUEST_MS0 (off)Logs one slow request line (level info, target tavrn_node::slow_request) for every HTTP request that took at least this many milliseconds (up to 3600000): method, route template (never ids), status, duration_ms and queries (the database statements it ran). See Performance.
TAVRN_DB_SLOW_STATEMENT_MS1000A database statement that takes at least this many milliseconds is logged as a warning (slow statement, target sqlx::query, with the statement text but no values); 0 turns it off (up to 3600000).

A few variables configure the compose setup around the node: the database password, the image tag, the host port and the optional Valkey password.

These are read by docker-compose.yml, not by the node:

VariableMeaning
POSTGRES_PASSWORDPassword of the compose PostgreSQL (required by the compose file).
IMAGE_TAGImage tag, e.g. 0.1 to pin a release line; default latest.
TAVRN_HOST_PORTHost port on 127.0.0.1 the reverse proxy points at (default 8080).
VALKEY_PASSWORDPassword of the optional valkey service (the same one goes into TAVRN_CACHE_URL).

This page is generated from tavrn-node/docs/HANDBOOK.md (AGPL-3.0-only) when the website is built; its links were adapted for this site.