Skip to main content

Configuration reference

This page lists all 175 variables of the node’s .env.example, grouped as in the file: 68 are set there, the others are commented out. Copy the file to .env next to docker-compose.yml and change what you need; the node checks the whole configuration at start and names every problem.

The middle column shows the value in the example file. A line that is commented out there shows the built-in default or an example: leave it commented out to keep the default.

How the configuration is loaded, and what the settings of each area mean, is explained on the Configuration page.

VariableValue in .env.exampleDescription
TAVRN_DOMAINchat.example.orgIdentity 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://chat.example.org (commented out)Identity domain vs. node host (Identity domain and node host (delegation)): TAVRN_DOMAIN is the identity (addresses alice@<domain>, keys, tokens). TAVRN_PUBLIC_URL is where the node is served, default https://<TAVRN_DOMAIN>. Example: addresses alice@tavrn.org, app at app.tavrn.org (the website on tavrn.org then serves or forwards /.well-known/tavrn):
Example: TAVRN_DOMAIN=tavrn.org
Example: TAVRN_PUBLIC_URL=https://app.tavrn.org
https://<host>[:port], no path; any host (http only with —dev)
POSTGRES_PASSWORDplease-use-a-long-random-passwordPassword of the compose PostgreSQL (required by the compose file).
VariableValue in .env.exampleDescription
IMAGE_TAG0.1 (commented out)Container image tag (docker-compose.yml): pin a release line such as 0.1 so docker compose pull only brings compatible updates; unset means the newest release (README.md “Upgrading”).
TAVRN_DATABASE_URLpostgres://tavrn:password@db.example.org:5432/tavrn (commented out)External PostgreSQL instead of the compose service “db” (then remove that service from docker-compose.yml):
postgres://user:password@host:port/db (or postgresql://). sqlite: only with --dev.
VariableValue in .env.exampleDescription
TAVRN_NAMEMy nodeDisplay name of the node (directory, discovery document).
TAVRN_DESCRIPTIONemptyShort description of the node (directory).
TAVRN_REGISTRATIONinviteopen | invite | closed (closed only blocks new accounts, never federation)
TAVRN_EMAILoptionalnone | optional | required (required needs TAVRN_SMTP_URL)
TAVRN_ADMIN_USERSemptye.g. alice,bob

Second factor (TOTP or passkeys) is mandatory for every account and cannot be switched off. TOTP secrets are encrypted with ./data/keys/secrets.key, stored files with ./data/keys/storage.key: back up ./data (including ./data/keys) together with the database. Passkeys: the WebAuthn origin defaults to TAVRN_PUBLIC_URL (override only for development/tests), the relying party id to TAVRN_DOMAIN. The relying party id must be the origin’s host or a parent domain of it (tavrn.org covers app.tavrn.org); keep it when the app host moves, or existing passkeys stop working. With a public URL on an unrelated host (chat.example.net for tavrn.org) set it to that host or its parent domain.

VariableValue in .env.exampleDescription
TAVRN_WEBAUTHN_ORIGINhttp://localhost:5173 (commented out)Origin of the web client for passkeys. Override only for development and tests (e.g. http://localhost:5173).
TAVRN_WEBAUTHN_RP_IDtavrn.org (commented out)Relying 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_ORIGINSandroid:apk-key-hash:<43 base64url characters> (commented out)Passkeys inside your Android app: its origin, the base64url SHA-256 of the APK’s signing certificate (comma-separated for several keys; Passkeys in the apps converts the hex fingerprint). The relying party’s assetlinks.json must list the same certificate.
VariableValue in .env.exampleDescription
TAVRN_SMTP_URLsmtp://192.0.2.10:8025 (commented out)Email for the node’s mails (address confirmation, password reset, account deletion, takedown notices, app token expiry), in each recipient’s language: smtp:// (plain, internal relay), smtps:// (TLS), smtp+starttls://
TAVRN_SMTP_FROMnoreply@example.org (commented out)default: noreply@<TAVRN_DOMAIN>
TAVRN_MAIL_TEMPLATES_DIR/data/templates/mail (commented out)Your own mail texts per language (English and German are built in): <dir>/<language>/<name>.txt, see Mail languages and templates and templates/mail/ of the repository.

Test the relay: docker exec tavrn tavrn-node send-test-mail you@example.org Captcha (optional): Cloudflare Turnstile, keys from dash.cloudflare.com → Turnstile

VariableValue in .env.exampleDescription
TAVRN_CAPTCHA_PROVIDERnonenone | turnstile
TAVRN_TURNSTILE_SITE_KEY0x4AAAAAAA... (commented out)See the introduction of this section.
TAVRN_TURNSTILE_SECRET_KEY0x4AAAAAAA... (commented out)See the introduction of this section.
TAVRN_CAPTCHA_REGISTERtruerequire the captcha on registration
TAVRN_CAPTCHA_LOGINfalserequire the captcha on login

Single sign-on via OpenID Connect (optional; APPLICATIONS.md §8, Single sign-on (OpenID Connect)). Register <TAVRN_PUBLIC_URL>/api/v1/auth/oidc/callback (default https://<TAVRN_DOMAIN>/…) as redirect URI at the provider (code flow, PKCE S256).

VariableValue in .env.exampleDescription
TAVRN_OIDC_ISSUERhttps://keycloak.example/realms/company (commented out)enables SSO; https only, must match exactly
TAVRN_OIDC_CLIENT_IDtavrn (commented out)Client id at the provider (required with an issuer).
TAVRN_OIDC_CLIENT_SECRET_FILE/data/keys/oidc-client-secret (commented out)File holding the client secret (preferred)
TAVRN_OIDC_CLIENT_SECRETempty (commented out)Or the secret itself (never printed); not both
TAVRN_OIDC_SCOPESopenid profile email groups (commented out)Requested scopes (space or comma separated); must contain openid.
TAVRN_OIDC_USERNAME_CLAIMpreferred_username (commented out)username of new accounts (sanitized, -2 on collision)
TAVRN_OIDC_DISPLAY_NAME_CLAIMname (commented out)updated on every login
TAVRN_OIDC_GROUPS_CLAIMgroups (commented out)Claim holding the groups (array of strings), from the ID token or UserInfo.
TAVRN_OIDC_BUTTON_LABELempty (commented out)empty: the client’s “Sign in with SSO”
TAVRN_OIDC_ONLYfalse (commented out)true: password/passkey logins and registration only for TAVRN_ADMIN_USERS
TAVRN_OIDC_AUTO_PROVISIONtrue (commented out)create accounts on the first SSO login
TAVRN_OIDC_GROUP_MAPempty (commented out)JSON, e.g. {“it-admins”:{“node_admin”:true},“staff”:{“room_creation”:5}}
TAVRN_OIDC_GROUP_SYNCadd (commented out)add | sync (sync also revokes what an earlier sync granted)
TAVRN_OIDC_SESSION_DAYS7 (commented out)hard end of an SSO session (1–30)
VariableValue in .env.exampleDescription
TAVRN_SESSION_ACCESS_MINUTES60 (commented out)access token lifetime (5–1440)
TAVRN_SESSION_REFRESH_DAYS90 (commented out)sliding refresh token lifetime (1–3650); SSO keeps TAVRN_OIDC_SESSION_DAYS as hard end
TAVRN_RATE_USER_PER_MINUTE1200 (commented out)API requests per user and minute (0 = off; route limits apply on top)
TAVRN_RATE_LIMIT_IPV6_PREFIX64 (commented out)per-address rate limits count IPv6 clients by this prefix (32-128)
TAVRN_PASSWORD_HASH_CONCURRENCY0 (commented out)password hashes running at once (0 = twice the CPU cores, at least 2)
TAVRN_WS_MAX_SOCKETS_PER_USER20 (commented out)WebSocket connections per user and instance (0 = unlimited)
TAVRN_DEVICE_STALE_DAYS180 (commented out)delete devices unused for this long (never the only one or one with a live session); 0 = never
TAVRN_E2EE_CLAIMS_PER_TARGET_PER_HOUR60 (commented out)claims for one user per hour per source (this node / one other node) by requesters sharing no room, DM or friendship with them (1–10000; 10x per user in total)
TAVRN_MINIMUM_AGE16 (commented out)minimum age at registration (13–99); the date of birth is asked on every path
VariableValue in .env.exampleDescription
TAVRN_ROOM_CREATIONinvitenone | invite | local | federated
TAVRN_MAX_ROOMS_PER_USER5Rooms a user may own (entitlement rooms.max_owned; 0–1000000, 0 = unlimited).
TAVRN_DM_POLICYeveryoneUpper bound of every user’s “Who can message me” (users choose within it, their default is everyone_shared): friends | everyone_shared (friends and shared rooms) | everyone. Non-friends land in the recipient’s message requests; blocks always apply. open is the old name of everyone.
TAVRN_UPLOADS_ENABLEDtruefalse switches file uploads off.
TAVRN_UPLOAD_MAX_BYTES26214400Largest 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 (wildcards like image/*); 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: images, audio, video, documents, archives, other (rooms set them per role and channel instead)
TAVRN_STORAGE_QUOTA_PER_ROOM_BYTES10737418240Storage per room (entitlement storage.max_bytes_per_room).
TAVRN_RETENTION_DAYS0Message retention: messages of channels, DMs and group DMs are deleted this many days after they were sent (hourly housekeeping, with their attachments). Pinned messages and the welcome channel are kept; rooms may only shorten it. 0 = keep forever (0-36500).
TAVRN_EMOJI_MAX_PER_ROOM100Custom emojis per room (entitlement emoji.max_per_room).
TAVRN_EMOJI_MAX_BYTES262144Largest custom emoji file.
TAVRN_PREVIEW_AUTO_MAX_BYTES5242880Images 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_STORAGE_ENCRYPTIONonEncryption at rest of every stored file (attachments, avatars, icons, emojis, sounds): each file has its own AES-256-GCM key, wrapped with ./data/keys/storage.key (created at the first start). BACK UP ./data/keys/storage.key with the database: without it every stored file is unreadable. Rotate it with the node stopped: tavrn-node rotate-storage-key (re-wraps the file keys only). off = new files are stored unencrypted (debugging only; encrypted files stay readable).

Entitlements (ENTITLEMENTS.md): the values above and below are the node defaults of the entitlement keys; 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. TAVRN_MAX_ROOMS_PER_USER (rooms.max_owned, 0 = unlimited), TAVRN_UPLOAD_MAX_BYTES (upload.max_bytes), TAVRN_STORAGE_QUOTA_PER_ROOM_BYTES (storage.max_bytes_per_room), TAVRN_EMOJI_MAX_PER_ROOM (emoji.max_per_room) are read from above.

VariableValue in .env.exampleDescription
TAVRN_UPLOAD_MAX_BYTES_CEILING0largest upload a grant can allow; 0 = TAVRN_UPLOAD_MAX_BYTES (grants cannot raise it)
TAVRN_VOICE_CHANNEL_MAX_PARTICIPANTS0voice.max_participants per voice channel (0 = only TAVRN_VOICE_MAX_PARTICIPANTS)
TAVRN_STREAM_MAX_HEIGHT0stream.max_height in pixels (0 = only TAVRN_SCREENSHARE_MAX_RESOLUTION); client-enforced
TAVRN_STREAM_MAX_FPS0stream.max_fps (0 = only TAVRN_SCREENSHARE_MAX_FPS); client-enforced
TAVRN_CAMERA_MAX_HEIGHT0camera.max_height in pixels (0 = only TAVRN_CAMERA_MAX_RESOLUTION); client-enforced
TAVRN_EMOJI_MAX_PERSONAL20emoji.max_personal
TAVRN_SOUNDS_MAX_PER_ROOM50sounds.max_per_room (soundboard clips)
TAVRN_SOUNDS_MAX_PERSONAL10sounds.max_personal
TAVRN_ATTACHMENT_RETENTION_DAYS0Attachment retention (attachments.retention_days): attachments of channels and DMs are deleted this many days after the upload (hourly housekeeping); messages keep listing them as “file expired”. Pinned messages keep their files. Rooms may only shorten it. 0 = keep forever.
TAVRN_FILES_CHANNELSonfiles_channels.enabled (on | off)
TAVRN_FILES_CHANNELS_MAX_BYTES10737418240files_channels.max_bytes per files channel on the node’s own storage (default 10 GiB); never limits external stores (each has its own optional limit in the channel’s storage settings). Every member who may attach files can fill it: 0 (unlimited) leaves the node’s disk unbounded. Grants and plans raise it per room.
TAVRN_FILES_EXTERNALoffFiles channels on external WebDAV/S3 stores (FILES.md §5): off by default. The private switch also allows private/loopback addresses and plain http (stores in the LAN; disables the SSRF protection for them).
on | off
TAVRN_FILES_EXTERNAL_PRIVATEoffon | off
TAVRN_FILES_INTERNALonoff = files channels on external stores only: the node keeps no files of files channels, a new channel takes uploads once its managers set a WebDAV/S3 store (needs TAVRN_FILES_EXTERNAL=on). Attachments in chats stay on the node. Node default of the entitlement files_channels.internal: a plan or override of a room owner can allow the node’s storage in their rooms anyway (1) or take it away on a node that is on (0).
on | off

Media pipeline for avatars, profile headers, room icons and video backgrounds. Uploads up to 20 MB and 30 s are converted to WebP (animated input to animated WebP with a still first frame) by ffmpeg without network access and with a 30-second limit per job. The image ships a minimal static ffmpeg.

VariableValue in .env.exampleDescription
TAVRN_MEDIA_FFMPEGautoauto (ffmpeg from PATH) | off (only static PNG/JPEG/WebP, no animations) | /path/to/ffmpeg
TAVRN_MEDIA_WORKERS1concurrent conversions (1–8); a few more uploads wait, the rest get 503 media.busy
TAVRN_MEDIA_CACHE_UNUSED_DAYS365cached emojis and soundboard clips of other nodes are dropped after this many days without use (0 = never)
TAVRN_MEDIA_CACHE_MAX_BYTES2147483648 (commented out)total size of those cached copies (least recently used go first)
TAVRN_MEDIA_CACHE_MAX_BYTES_PER_HOST67108864 (commented out)size of the cached copies from one origin node
TAVRN_WHITEBOARD_MAX_SCENES50whiteboard scenes per user in the encrypted library (1–1000, at most 5 MB each)

GIF search (optional): the API key stays on the node, clients only ask the node

VariableValue in .env.exampleDescription
TAVRN_GIF_PROVIDERnonenone | giphy | tenor
TAVRN_GIF_API_KEYempty (commented out)Required with a GIF provider.

Link 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.

VariableValue in .env.exampleDescription
TAVRN_UNFURLonon | off
VariableValue in .env.exampleDescription
TAVRN_AUTOMOD_BURST_LIMIT10more than N messages per user and channel in 5 seconds = spam (0 = off)
TAVRN_AUTOMOD_TIMEOUT_MINUTES10Timeout in minutes after a spam burst.
TAVRN_AUTOMOD_WORDS_FILE/data/automod/words.txt (commented out)Content rules of the operator for every unencrypted conversation (MODERATION.md §2.3), action “block”. Both files are read at start and again every hour and on SIGHUP (docker compose kill -s HUP tavrn); an invalid file keeps the previous lists. One entry per line, # comments, at most 1 MiB / 10000 entries.
terms, matched as whole words; * matches within a word
TAVRN_AUTOMOD_LINKSoff (commented out)off | path of a domain blocklist (subdomains included)
VariableValue in .env.exampleDescription
TAVRN_REPORTS_RATE_PER_HOUR10 (commented out)reports per user and hour (1-1000)
TAVRN_REPORTS_RETENTION_DAYS90 (commented out)resolved and dismissed reports are deleted after this many days (1-36500)
TAVRN_REPORTS_INBOUND_PER_HOUR20 (commented out)reports forwarded by other nodes per sending domain and hour, 5x in total (1-1000)

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

VariableValue in .env.exampleDescription
TAVRN_APP_CREATIONall (commented out)all | invite (admin grants per user) | admins
TAVRN_APPS_PER_USER5 (commented out)applications per owner (0-100)
TAVRN_APPS_PER_ROOM25 (commented out)installed applications per room (0-1000)
TAVRN_APP_TOKEN_MAX_DAYS365 (commented out)maximum app token lifetime (1-3650 days)
TAVRN_APP_RATE_REQUESTS1200 (commented out)API requests per application and minute
TAVRN_APP_ALLOW_PRIVATE_URLSfalse (commented out)application URLs on private/loopback addresses (intranets; SSRF protection off)
TAVRN_APP_COMMANDS_REQUIRE_APPROVALfalse (commented out)admins approve every application command definition (Admin API, §5.2)
TAVRN_COMPONENT_MAX_AGE_DAYS30 (commented out)buttons and menus of application messages older than this answer 410 (0 = no limit)

Incoming webhooks (APPLICATIONS.md §6.1): write-only URLs that post into unencrypted channels.

VariableValue in .env.exampleDescription
TAVRN_WEBHOOKS_ENABLEDtrue (commented out)Incoming webhooks; false refuses creation and execution (webhook.disabled).
TAVRN_WEBHOOKS_PER_CHANNEL10 (commented out)1-50 per channel, at most 50 per room

Message search over unencrypted channels (PROTOCOL.md §7d, SEARCH.md); encrypted conversations are searched in the clients’ local encrypted index only.

VariableValue in .env.exampleDescription
TAVRN_SEARCH_ENABLEDtrue (commented out)Node-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_MINUTE30 (commented out)search requests per user and minute (1-10000)

Legal (LEGAL.md, Legal pages and GDPR): takedown notices, terms acceptance, account deletion.

VariableValue in .env.exampleDescription
TAVRN_LEGAL_NOTICESoff (commented out)on: public notice form /legal/notice (captcha when configured)
TAVRN_LEGAL_NOTICE_RATE_PER_HOUR5 (commented out)notices per client address and hour (1-1000)
TAVRN_LEGAL_NOTICE_RETENTION_DAYS1095 (commented out)cases are kept this long after their last change (30-36500)
TAVRN_LEGAL_NOTICE_INBOUND_PER_HOUR10 (commented out)notices forwarded by other nodes per sending domain and hour, 5x in total (1-1000)
TAVRN_LEGAL_REPEAT_THRESHOLD3 (commented out)actioned notices after which the admin page suggests disabling (1-100)
TAVRN_LEGAL_COUNTER_NOTICE_DAYS14 (commented out)days the uploader may send a counter-notice (1-365)
TAVRN_LEGAL_TERMS_MODEnotice (commented out)notice: ask once after a new version; require: block until accepted
TAVRN_ACCOUNT_DELETE_GRACE_DAYS30 (commented out)deleted accounts can be restored this long; then erased (1-365)

Voice, video and screen sharing (VOICE.md). The node starts and supervises livekit-server (part of the image) with a config generated from these values; its API key lives in ./data/keys/livekit.key.

VariableValue in .env.exampleDescription
TAVRN_VOICE_ENABLEDtruefalse: no voice channels, no LiveKit process
TAVRN_VOICE_MAX_BITRATE_KBPS192audio bitrate cap for every voice channel and call (8–512); 192 allows the music preset
TAVRN_VOICE_MAX_PARTICIPANTS50participants per voice channel at most (1–1000)
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 | 4k
TAVRN_SCREENSHARE_MAX_FPS301–60
TAVRN_CAMERA_ENABLEDtrueWebcam sharing (the use_camera permission still applies).
TAVRN_CAMERA_MAX_RESOLUTION720p360p | 720p | 1080p
TAVRN_CAMERA_MAX_FPS301–60
TAVRN_RTC_UDP_PORT_RANGE61000-61100Media ports: open this UDP range in the firewall and publish it 1:1 to the container (docker-compose.yml). Keep the range outside the Linux ephemeral port range (32768-60999): otherwise any process’s outgoing UDP socket can occupy a port in it and the container fails to publish the range.
TAVRN_RTC_EXTERNAL_IP203.0.113.7 (commented out) or home.dyndns.example (commented out)Addresses LiveKit announces to clients (VOICE.md §10.2). Leave both empty to detect the public IPv4 and IPv6 via STUN at start (recommended when the IPv6 prefix changes). Explicit: the first IPv4 and the first IPv6 replace the container’s private addresses; every further address (e.g. the host’s LAN address, for clients in the same network when the router has no NAT loopback) is handed to clients as an extra candidate. The startup log line “LiveKit starting media=…” names what is announced. Both also take hostnames (dynamic DNS, mixed with IP literals): resolved at start and every minute (or after the DNS TTL if longer), A and AAAA; LiveKit restarts when the addresses change (calls reconnect within the grace period). Use an unproxied record with a low TTL; media cannot go through Cloudflare & co (Voice and video).
TAVRN_RTC_ADVERTISE_IPS2001:db8::7,192.168.0.23 (commented out) or home.dyndns.example,192.168.0.23 (commented out)More 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_PRIVATEfalse (commented out)true: private addresses (LAN, ULA) of hostnames are announced too
TAVRN_RTC_STUN_SERVERSstun.l.google.com:19302 (commented out)host:port list for the detection (default: LiveKit’s)
TAVRN_RTC_TCP_PORT7881ICE over TCP when UDP is blocked: publish and forward this TCP port like the UDP range (0 = off).
TAVRN_RTC_TURN_ENABLEDfalseEmbedded TURN server for clients behind strict NATs/firewalls (TURN over TLS through the proxy).
TAVRN_RTC_TURN_UDP_PORT3478 (commented out)TURN over UDP, published and forwarded (0 = off)
TAVRN_RTC_TURN_TLS_PORT5349 (commented out)plain TCP behind the proxy’s TLS termination on 443 (0 = off)
TAVRN_RTC_TURN_DOMAINturn.chat.example.org (commented out)default turn.<host of TAVRN_PUBLIC_URL>; the proxy serves it on 443
TAVRN_RTC_TURN_RELAY_PORT_RANGE30000-30999 (commented out)relay sockets inside the container, not published
TAVRN_LIVEKIT_PORT7880 (commented out)LiveKit’s signaling/API port on 127.0.0.1 inside the container (never published; clients use /rtc).
TAVRN_LIVEKIT_BINARYlivekit-server (commented out)path or name in PATH (outside Docker: just livekit-install)
VariableValue in .env.exampleDescription
TAVRN_FEDERATION_ENABLEDtruefalse: no tokens, federated logins, inbox or outbox
TAVRN_REFERENCE_NODEStavrn.orgPeer directory and Discover (FEDERATION.md §7): reference nodes this node announces itself to (at start, every 6 hours and when a room is listed or unlisted) and pulls the node list from; comma-separated. The template lists the node at the official node tavrn.org and pulls its node list; empty = no reference nodes (the node is in no directory and its Discover page shows only nodes that announce themselves to it).
TAVRN_DIRECTORY_LISTINGtruefalse: never announce; other nodes refuse to list this node
TAVRN_DIRECTORY_MAX_ROOMS5000 (commented out)cached rooms of other nodes for Discover (at most 200 per node)
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_PRIVATEfalse (commented out)true allows peers on private/loopback addresses (dev, tests, intranets)
TAVRN_FEDERATION_SRV_FALLBACKtrue (commented out)Resolution order of other nodes: well-known -> SRV -> https://<domain>. When a peer’s identity domain serves no https://<domain>/.well-known/tavrn (404, or no HTTPS server at all), look up the SRV record _tavrn._tcp.<domain> and fetch the document from its target before trying https://<domain>/api/v1 (browsers get the same result through this node, GET /api/v1/federation/resolve/<domain>). The target must present a certificate valid for <domain> itself (TLS and Host are <domain>); a certificate only for the target’s own name is refused.
TAVRN_FEDERATION_REMOTE_BOTStrue (commented out)accept bots of other nodes
TAVRN_APPS_FEDERATEtrue (commented out)this node’s bots may obtain federation tokens for other nodes
TAVRN_FEDERATION_RATE_LOGINS60 (commented out)Per remote domain and minute:
Federated 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_INBOX300 (commented out)Per remote domain and minute:
Inbox messages per remote domain and minute (> 0).
TAVRN_FEDERATION_RATE_API3000 (commented out)Per remote domain and minute:
API requests of all users of one remote domain per minute (> 0).
TAVRN_FEDERATION_RATE_API_PER_USER600 (commented out)Per remote domain and minute:
per remote user, below the domain’s budget
TAVRN_FEDERATION_RATE_CLAIMS120 (commented out)Per remote domain and minute:
KeyPackage claims per requesting node and minute (> 0).
TAVRN_FEDERATION_MAX_REMOTE_USERS10000 (commented out)Remote user quotas:
Remote 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_DOMAIN1000 (commented out)Remote user quotas:
Remote users per home domain.
TAVRN_FEDERATION_PERMIT_TIMEOUT_SECS10 (commented out)Remote user quotas:
wait for a connection slot of a peer, then node-wide (1-120)
TAVRN_FEDERATION_MAX_UNVERIFIED_PEERS1000 (commented out)Remote user quotas:
peers known only from unverified requests (10-1000000)
TAVRN_FEDERATION_DISCOVERY_SLOTS12 (commented out)Remote user quotas:
parallel discovery fetches for unknown, unverified issuers (1-64)
TAVRN_FEDERATION_REMOTE_USER_RETENTION_DAYS90 (commented out)Remote 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_BANSfalse (commented out)true: 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_CONFORMANCEfalse (commented out)Conformance checks of other nodes (FEDERATION.md §9) always run (first contact, then daily; hard failures and observed spoofing block a node automatically). true: also refuse nodes that were not checked yet or failed. Check a node by hand: tavrn-node doctor --peer <domain> or the Federation tab of the node administration.
TAVRN_FEDERATION_DEV_PEERSa.test=http://127.0.0.1:18081,b.test=http://127.0.0.1:18082 (commented out)Development only (refused without —dev): local multi-node setups
TAVRN_DEV_MISBEHAVEempty (commented out)Development 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; comma-separated). Never in production.

Operations (TLS is done by the reverse proxy; tavrn speaks HTTP only)

VariableValue in .env.exampleDescription
TAVRN_LISTEN0.0.0.0:8080Address and port of the plain-HTTP server.
TAVRN_HOST_PORT8080local host port the reverse proxy points at
TAVRN_TRUSTED_PROXIES127.0.0.0/8,::1/128,172.16.0.0/12,192.168.0.0/16 (commented out)Proxies whose X-Forwarded-For is trusted (IP or CIDR). Default: loopback only. docker-compose.yml sets Docker’s private ranges (the proxy reaches the node through Docker’s gateway); list only your proxies, never client networks.
TAVRN_LOG_LEVELinfoA 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 | json
TAVRN_LOG_SLOW_REQUEST_MS0 (commented out)Performance (Performance): log every HTTP request that took at least this many milliseconds (method, route template, status, duration_ms, queries = database statements); 0 = off (default).
TAVRN_DB_SLOW_STATEMENT_MS1000 (commented out)Log a warning for every database statement that took at least this many milliseconds; 0 = off.
TAVRN_DATA_DIR/data (commented out)Directory for keys/, blobs/ (stored files) and the generated LiveKit configuration; the image sets /data.
TAVRN_CONFIG/data/tavrn.toml (commented out)Optional TOML file with further settings (keys are the variable names without TAVRN_ in lowercase); the environment wins over the file.
TAVRN_DEVfalse (commented out)Development mode, the same as the —dev flag (SQLite, development defaults, How the configuration is loaded). Never in production.

Web push notifications to browsers and installed web apps (Web push): on by default. The node creates its VAPID key in /data/keys/vapid.key (back it up with the other keys) and needs outbound HTTPS to the browsers’ push services. A push goes out only while the user has no open connection.

VariableValue in .env.exampleDescription
TAVRN_PUSHon (commented out)on | off
TAVRN_PUSH_CONTACTmailto:admin@example.org (commented out)VAPID contact (mailto: or https:); default: the public URL
TAVRN_PUSH_MAX_PER_MINUTE10 (commented out)per user (1-1000); plus at most 1 per conversation per 30 s

Prometheus metrics at GET /metrics (Metrics and health): off by default. With a token (16-256 printable characters) scrapers send “Authorization: Bearer <token>”; without one only loopback and TAVRN_TRUSTED_PROXIES clients are answered. Block /metrics at the reverse proxy for the public.

VariableValue in .env.exampleDescription
TAVRN_METRICSoff (commented out)on | off
TAVRN_METRICS_TOKENempty (commented out)When 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.

Several instances of this node behind a load balancer (Scaling with Valkey). Unset: one instance, everything in memory (the default; Valkey is not needed). Set: every instance shares PostgreSQL, the /data volume and this Valkey/Redis (>= 7); the node refuses to start (exit code 3) while it is unreachable. rediss:// for TLS; the password never appears in logs or check-config. Also uncomment the “valkey” service in docker-compose.yml.

VariableValue in .env.exampleDescription
VALKEY_PASSWORDchange-me (commented out)the “valkey” service’s password
TAVRN_CACHE_URLredis://:change-me@valkey:6379/0 (commented out)same password (URL-encode special characters)
TAVRN_INSTANCE_IDtavrn-1 (commented out)Name of this instance in logs and the cache (A-Z a-z 0-9 . _ -, at most 64); default: random per start.
TAVRN_INSTANCE_URLhttp://tavrn-1:8080 (commented out)Where the other instances reach this one over HTTP (voice, call and whiteboard requests are relayed to the leader instance); default: this host’s address towards the cache with the TAVRN_LISTEN port.

This page is generated from tavrn-node/deploy/.env.example (AGPL-3.0-only) when the website is built.