mirror of
https://github.com/warmbly/warmbly.git
synced 2026-08-18 16:01:18 +00:00
551 lines
23 KiB
Bash
551 lines
23 KiB
Bash
# ==============================================================================
|
|
# Warmbly environment
|
|
# ==============================================================================
|
|
# Copy this file to .env next to docker-compose.yml:
|
|
#
|
|
# cp .env.example .env
|
|
#
|
|
# You do not need it to start. `make up` boots the whole platform with no .env
|
|
# at all and prints a link that claims the instance. Copy this file when you are
|
|
# ready to run something other people can reach.
|
|
#
|
|
# Everything here is read from the process environment. The environment is
|
|
# authoritative: no screen in the product ever overwrites a value you set here.
|
|
# The admin panel on :5174 shows the backend process's resolved value for most
|
|
# of these under Instance > Configuration, with its source and whether a change
|
|
# needs a restart. It does not read the realtime or tracking containers, so the
|
|
# variables in those two blocks below are not on that page.
|
|
#
|
|
# Full reference, with defaults and restart requirements per variable:
|
|
# https://docs.warmbly.com/development/configuration/
|
|
#
|
|
# ------------------------------------------------------------------------------
|
|
# HOW EMPTY VALUES BEHAVE, BECAUSE IT IS NOT WHAT YOU EXPECT
|
|
#
|
|
# docker-compose.yml reads this file as ${VAR:-default}, and Compose treats an
|
|
# EMPTY assignment exactly like a missing one. So `KMS_LOCAL_MASTER_KEY=` does
|
|
# not blank the key, it substitutes the published default from
|
|
# docker-compose.yml. To leave a value unset, comment the line out; to change
|
|
# it, give it a real value. There is no way to force "empty" here.
|
|
# ------------------------------------------------------------------------------
|
|
# CHANGE THESE FIVE BEFORE ANYONE ELSE CAN REACH THIS INSTANCE
|
|
#
|
|
# AUTH_SECRET INTERNAL_API_TOKEN SECRET_KEY_BASE
|
|
# KMS_LOCAL_MASTER_KEY CREDENTIALS_ENCRYPTION_KEY
|
|
#
|
|
# docker-compose.yml ships a working default for each so a fresh clone boots.
|
|
# All five defaults are published in this repository, so they protect nothing:
|
|
# anyone can forge a session token or unwrap every organization key. They ship
|
|
# commented out below, so this file boots as-is on those published defaults and
|
|
# the backend logs a warning. Generate real values (the Secrets block has the
|
|
# commands), uncomment the five lines, THEN uncomment APP_ENV=prod: with prod
|
|
# set, the backend refuses to start while any of the five is still a default.
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
# === Deployment ===============================================================
|
|
|
|
# dev | prod. dev tolerates the published default secrets and turns on debug
|
|
# logging. prod needs no cloud account. Uncomment this only after the five
|
|
# secrets below hold real values, or the backend refuses to start.
|
|
# APP_ENV=prod
|
|
|
|
# cloud | self_hosted. Picks the auth defaults in the Auth policy block below;
|
|
# every one stays individually overridable.
|
|
DEPLOYMENT_MODE=self_hosted
|
|
|
|
# true lets the backend boot on a published default secret even under
|
|
# APP_ENV=prod. For a throwaway instance only. The shipped docker-compose.yml
|
|
# does not forward it, so under compose it only works if you add it there.
|
|
# ALLOW_INSECURE_DEFAULTS=false
|
|
|
|
# debug | release. The shipped docker-compose.yml pins the backend to release,
|
|
# so this line only takes effect outside compose.
|
|
# GIN_MODE=release
|
|
|
|
# Label shown next to the instance name in the admin panel.
|
|
# ENV_LABEL=production
|
|
|
|
# true lets customer webhooks point at http:// and private addresses, which
|
|
# lets any workspace member make the backend reach into your network.
|
|
# Development only. Not forwarded by the shipped docker-compose.yml.
|
|
# WARMBLY_ALLOW_UNSAFE_WEBHOOK_URLS=false
|
|
|
|
|
|
# === Secrets (CHANGE ALL FIVE) ================================================
|
|
#
|
|
# Generate all five at once and append them to this file:
|
|
#
|
|
# cat >> .env <<EOF
|
|
# AUTH_SECRET=$(openssl rand -base64 32)
|
|
# INTERNAL_API_TOKEN=$(openssl rand -hex 24)
|
|
# SECRET_KEY_BASE=$(openssl rand -base64 64 | tr -d '\n')
|
|
# KMS_LOCAL_MASTER_KEY=$(openssl rand -base64 32)
|
|
# CREDENTIALS_ENCRYPTION_KEY=$(openssl rand -hex 32)
|
|
# EOF
|
|
#
|
|
# `make gen-key` prints a single fresh KMS_LOCAL_MASTER_KEY if that is all you
|
|
# need. Every line below is commented out on purpose: an uncommented placeholder
|
|
# would be just as public as the compose default while hiding from the check
|
|
# that refuses to boot on one.
|
|
|
|
# JWT and session signing. 32 characters minimum. MUST equal the realtime
|
|
# service's JWT_SECRET (compose derives that from this value for you).
|
|
# AUTH_SECRET=
|
|
|
|
# Shared token for the backend's /api/v1/internal/ routes. Workers read it as
|
|
# ENCRYPTED_KEYS_WORKER_TOKEN and the tracking service reads it directly.
|
|
# Unset fails closed: workers cannot fetch keys, tracking cannot resolve links.
|
|
# INTERNAL_API_TOKEN=
|
|
|
|
# Phoenix session signing in the realtime service. 64 characters minimum.
|
|
# SECRET_KEY_BASE=
|
|
|
|
# Root key that seals every per-organization data key. base64, exactly 32 bytes.
|
|
# BACK IT UP. Losing it is unrecoverable.
|
|
# KMS_LOCAL_MASTER_KEY=
|
|
|
|
# Seals mailbox SMTP and IMAP passwords at rest. Exactly 64 hex characters.
|
|
# BACK IT UP. Set it before you connect a single mailbox. Outside compose an
|
|
# unset value does not fail at boot, it stores credentials unsealed; under
|
|
# compose an unset or empty value takes the published default instead.
|
|
# CREDENTIALS_ENCRYPTION_KEY=
|
|
|
|
|
|
# === Addresses ================================================================
|
|
#
|
|
# APP_URL is the source of every emailed link: password resets, invitations and
|
|
# the first-run claim link. Leave it unset and those links are built against the
|
|
# hosted service, which sends a live reset token off your deployment.
|
|
|
|
# With the shipped docker-compose.yml, set PUBLIC_HOST to your LAN IP or domain
|
|
# and APP_URL / API_PUBLIC_URL / CORS_ALLOW_ORIGINS / WEBSOCKET_URL /
|
|
# TRACKING_DOMAIN / PHX_HOST and the frontend URLs all derive from it. Note that
|
|
# setting it turns localhost off, because a localhost origin is then no longer
|
|
# allowed by CORS. Setting any of the six below by hand opts that one out of the
|
|
# derivation, which is why they ship commented out.
|
|
# PUBLIC_HOST=192.168.1.50
|
|
|
|
# The compose defaults, shown so you can see what PUBLIC_HOST would replace.
|
|
# APP_URL=http://localhost:5173
|
|
# API_PUBLIC_URL=http://localhost:8080
|
|
# CORS_ALLOW_ORIGINS=http://localhost:5173,http://localhost:5174
|
|
# WEBSOCKET_URL=ws://localhost:4000/socket/websocket
|
|
|
|
# The listen address. The shipped docker-compose.yml pins it, so this line only
|
|
# takes effect outside compose.
|
|
# API_HOST=0.0.0.0:8080
|
|
|
|
# Exact origin the mailbox OAuth callback page posts the authorization code back
|
|
# to. Unset means a wildcard target origin. Not forwarded by the shipped
|
|
# docker-compose.yml.
|
|
# APP_ORIGIN=http://localhost:5173
|
|
|
|
# Domain serving open pixels and click links. Use a separate, neutral domain in
|
|
# production and proxy it to the tracking service on :3000.
|
|
# TRACKING_DOMAIN=localhost:3000
|
|
|
|
# Host port the tracking service is published on. 3000 is a very common default
|
|
# for other self-hosted tools, so remap it here when it is already taken and
|
|
# point your reverse proxy at the new port. The container port stays 3000.
|
|
# TRACKING_PORT=3001
|
|
|
|
# Behind a reverse proxy with HTTPS, set each one explicitly instead:
|
|
# APP_URL=https://app.example.com
|
|
# API_PUBLIC_URL=https://api.example.com
|
|
# CORS_ALLOW_ORIGINS=https://app.example.com,https://admin.example.com
|
|
# WEBSOCKET_URL=wss://ws.example.com/socket/websocket
|
|
# PHX_HOST=ws.example.com
|
|
# TRACKING_DOMAIN=t.example.com
|
|
|
|
|
|
# === Network and proxy ========================================================
|
|
|
|
# CIDRs allowed to set X-Forwarded-For. Empty trusts nothing, which is correct
|
|
# for a directly exposed backend. Behind a proxy it is not: with nothing
|
|
# trusted, the login rate limiter, session records, audit rows and API key IP
|
|
# allowlists all record the proxy's address instead of the client's.
|
|
# TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12
|
|
|
|
|
|
# === Database, cache and GeoIP ================================================
|
|
|
|
PRIMARY_DB=postgres://warmbly:warmbly@postgres:5432/warmbly_dev?sslmode=disable
|
|
REDIS=redis://redis:6379
|
|
# Pool size for the realtime service only. The Go services use the driver
|
|
# default.
|
|
# DATABASE_POOL_SIZE=10
|
|
|
|
# Must be SET on the backend in every environment (an unset value is fatal at
|
|
# boot). The file it points at is optional: without it, sessions and audit rows
|
|
# are recorded with no city and nothing else changes.
|
|
GEODB_PATH=/app/data/GeoLite2-City.mmdb
|
|
|
|
|
|
# === Auth policy ==============================================================
|
|
#
|
|
# Self-host defaults. Each is independently overridable.
|
|
# https://docs.warmbly.com/development/accounts-and-access/
|
|
|
|
# always | new_device | off. Self-host defaults to off, so a login never depends
|
|
# on outbound mail. Registration and reset codes still use email, which is what
|
|
# NIST SP 800-63B permits email for.
|
|
# AUTH_LOGIN_CODE=off
|
|
|
|
# REQUIRE_EMAIL_VERIFICATION=false
|
|
|
|
# true | false | invite_only. Self-host defaults to invite_only.
|
|
#
|
|
# false anyone may create an account from the sign-up form
|
|
# invite_only the sign-up form is refused, but an invitation link creates the
|
|
# account: the link carries the token that permits the signup.
|
|
# A stranger without one cannot register.
|
|
# true signups are off, and members cannot mint invitations either
|
|
#
|
|
# There is one exemption, and it does not apply to `true`: under false or
|
|
# invite_only, while the users table is EMPTY any signup is allowed, which is
|
|
# what makes a fresh instance claimable. Seeding spends it permanently.
|
|
# DISABLE_REGISTRATION=invite_only
|
|
|
|
# Turns off email and password entirely. Only do this with a working provider
|
|
# configured, or there is no way to sign in at all.
|
|
# DISABLE_PASSWORD_LOGIN=false
|
|
|
|
# true lets a verified identity-provider assertion create an account regardless
|
|
# of DISABLE_REGISTRATION. Off by default so configuring OIDC never silently
|
|
# reopens signup.
|
|
# SSO_AUTO_PROVISION=false
|
|
|
|
# Unauthenticated auth requests per source IP per 15 minutes.
|
|
# AUTH_IP_RATE_LIMIT=60
|
|
|
|
# Encrypts stored TOTP secrets. Falls back to AUTH_SECRET when unset. Not
|
|
# forwarded by the shipped docker-compose.yml.
|
|
# TWOFA_SECRET=
|
|
|
|
# Passkeys (WebAuthn). Derived from APP_URL when unset. Changing the RP ID
|
|
# invalidates every enrolled passkey, so keep it stable per deployment.
|
|
# Passkeys need a secure context: HTTPS, or a localhost address.
|
|
# WEBAUTHN_RP_DISPLAY_NAME is not forwarded by the shipped docker-compose.yml.
|
|
# WEBAUTHN_RP_ID=app.example.com
|
|
# WEBAUTHN_RP_ORIGINS=https://app.example.com
|
|
# WEBAUTHN_RP_DISPLAY_NAME=Warmbly
|
|
|
|
|
|
# === First owner ==============================================================
|
|
#
|
|
# Read ONLY while the users table is empty, so these are a no-op on every later
|
|
# restart and cannot be used to fix a lockout. Leave unset and the backend
|
|
# prints a single-use setup link instead (`make claim` prints it again).
|
|
#
|
|
# Produce the hash with `warmblyctl hash-password`, then QUOTE IT here: an
|
|
# argon2 PHC string contains $ characters and compose reads those as
|
|
# interpolation, which silently mangles an unquoted value.
|
|
# WARMBLY_BOOTSTRAP_EMAIL=you@example.com
|
|
# WARMBLY_BOOTSTRAP_PASSWORD_HASH='$argon2id$v=19$m=65536,t=3,p=2$...'
|
|
# WARMBLY_BOOTSTRAP_ORG=My Organization
|
|
#
|
|
# Plaintext convenience form. Warns at boot and leaves the password readable
|
|
# through `docker inspect`. Prefer the hash, and remove it once the owner exists.
|
|
# WARMBLY_BOOTSTRAP_PASSWORD=
|
|
|
|
|
|
# === Single sign-on (generic OpenID Connect) ==================================
|
|
#
|
|
# The only sign-in path with no dependency on outbound mail, so it is the
|
|
# recommended posture for a deployment with no relay. Works with Authentik,
|
|
# Keycloak, Zitadel, Pocket ID, Dex and anything else publishing a discovery
|
|
# document. RS256 ID tokens only.
|
|
#
|
|
# The redirect URI is /v1/auth/oidc/callback, NOT /api/v1/auth/oidc/callback.
|
|
# Providers match it by exact string.
|
|
# OIDC_ISSUER_URL=https://id.example.com/application/o/warmbly/
|
|
# OIDC_CLIENT_ID=
|
|
# OIDC_CLIENT_SECRET=
|
|
# OIDC_REDIRECT_URL= # defaults to API_PUBLIC_URL/v1/auth/oidc/callback
|
|
# OIDC_SCOPES=openid,profile,email
|
|
# OIDC_ALLOWED_DOMAINS=example.com # without this, any address your IdP asserts
|
|
# OIDC_DEFAULT_ORG= # org every SSO user joins; without it each
|
|
# # new user gets their own single-member org
|
|
# OIDC_PROVIDER_NAME=Single sign-on
|
|
|
|
|
|
# === Social sign-in (optional) ================================================
|
|
#
|
|
# Separate from the BOX_GOOGLE_* mailbox client further down. These are for
|
|
# logging INTO Warmbly; those are for sending FROM a mailbox.
|
|
# GOOGLE_CLIENT_ID=
|
|
# GOOGLE_CLIENT_SECRET=
|
|
# GOOGLE_REDIRECT_URI=https://app.example.com/auth/google/callback
|
|
# GOOGLE_IOS_CLIENT_ID=
|
|
# APPLE_APP_ID=
|
|
# APPLE_TEAM_ID=
|
|
# APPLE_KEY_ID=
|
|
# APPLE_KEY_SECRET=
|
|
|
|
|
|
# === Captcha ==================================================================
|
|
#
|
|
# turnstile with an empty secret fails every verification, which means nobody
|
|
# can sign in. Set both, or leave the provider at none.
|
|
#
|
|
# CAPTCHA_PROVIDER is derived when unset: turnstile if TURNSTILE_SECRET holds a
|
|
# value, none otherwise. Set it explicitly only to force turnstile on.
|
|
# CAPTCHA_PROVIDER=none
|
|
# TURNSTILE_SECRET=
|
|
# WARMBLY_TURNSTILE_KEY= # the site key, read by web + admin
|
|
# TURNSTILE_BYPASS_TOKEN= # only honoured when APP_ENV=dev; not
|
|
# # forwarded by the shipped compose file
|
|
|
|
|
|
# === Platform mail (login codes, resets, invitations, digests) ================
|
|
#
|
|
# MAIL_TRANSPORT is the primary switch: smtp | log | ses.
|
|
#
|
|
# log writes every message to the backend log and delivers nothing. The
|
|
# COMPOSE default, so a fresh install works with no relay. Invitations
|
|
# still work: copy the link from Settings > Members and send it yourself.
|
|
# smtp a real submission relay. Set SMTP_HOST plus the credentials below.
|
|
# ses AWS SES; needs AWS credentials and a verified identity.
|
|
#
|
|
# A bare binary with neither MAIL_TRANSPORT nor SMTP_HOST falls back to ses.
|
|
MAIL_TRANSPORT=log
|
|
|
|
# The backend refuses to start without these two. The consumer only WARNS and
|
|
# then silently sends no notification or digest mail, so set them everywhere.
|
|
EMAIL_NAME=Warmbly
|
|
EMAIL_ADDRESS=noreply@example.com
|
|
|
|
# SMTP_SECURITY is starttls (587) | tls (465) | none (25). The port follows the
|
|
# mode, so setting either alone is enough. Credentials are never sent over an
|
|
# unencrypted connection.
|
|
# SMTP_HOST=smtp.example.com
|
|
# SMTP_PORT=587
|
|
# SMTP_USERNAME=
|
|
# SMTP_PASSWORD=
|
|
# SMTP_SECURITY=starttls
|
|
# SMTP_AUTH=auto # auto | plain | login | cram-md5 | none
|
|
# SMTP_EHLO_NAME= # defaults to the sender domain
|
|
# SMTP_TLS_INSECURE_SKIP_VERIFY=false # only for a relay with a private CA
|
|
|
|
# Notification volume.
|
|
# NOTIFICATION_EMAIL_DAILY_CAP=25 # per user per day; 0 means uncapped
|
|
# NOTIFICATION_PUSH_WINDOW=5h
|
|
|
|
|
|
# === Transactional email branding =============================================
|
|
# A self-hosted install should not send mail attributed to another company.
|
|
# EMAIL_BRAND_NAME=Acme
|
|
# EMAIL_BRAND_LEGAL_ENTITY=Acme Ltd
|
|
# EMAIL_BRAND_COMPANY_NUMBER=
|
|
# EMAIL_BRAND_PLACE_OF_REG=
|
|
# EMAIL_BRAND_ADDRESS=
|
|
# EMAIL_BRAND_WEBSITE_URL=https://acme.example.com
|
|
# EMAIL_BRAND_SUPPORT_EMAIL=support@acme.example.com
|
|
# EMAIL_BRAND_TERMS_URL=
|
|
# EMAIL_BRAND_PRIVACY_URL=
|
|
|
|
|
|
# === Provider switches ========================================================
|
|
#
|
|
# Every provider default IN CODE is the cloud one (kafka, avro, s3, aws), so a
|
|
# deployment that is not using the shipped docker-compose.yml must set all five
|
|
# explicitly or the process exits at boot.
|
|
|
|
# true reads secrets from AWS SSM / Secrets Manager.
|
|
AWS_CONFIG_ENABLED=false
|
|
|
|
# Event bus. nats: one small JetStream binary. kafka: build the images with
|
|
# GO_TAGS=kafka / CARGO_FEATURES=kafka and set KAFKA_* below.
|
|
EVENTBUS_PROVIDER=nats
|
|
NATS_URL=nats://nats:4222
|
|
# NATS_STREAM_NAME=warmbly
|
|
# NATS_SUBJECT_PREFIX=warmbly
|
|
# KAFKA_BOOTSTRAP_SERVERS=broker:9092
|
|
# KAFKA_SASL_USERNAME=
|
|
# KAFKA_SASL_PASSWORD=
|
|
# SCHEMA_REGISTRY_URL=
|
|
# SCHEMA_REGISTRY_KEY=
|
|
# SCHEMA_REGISTRY_SECRET=
|
|
# EVENTBUS_HANDLER_TIMEOUT=30s
|
|
|
|
# Serialization. json is REQUIRED wherever workers run: worker command and
|
|
# result envelopes carry untyped bodies Avro cannot serialize.
|
|
CODEC_PROVIDER=json
|
|
|
|
# Encryption root key provider. local: the AES master key above. aws: AWS KMS.
|
|
KMS_PROVIDER=local
|
|
# Instead of the inline key. Not forwarded by the shipped docker-compose.yml.
|
|
# KMS_LOCAL_MASTER_KEY_FILE=/run/secrets/kms_master_key
|
|
# KMS_AWS_KEY_ID=alias/warmbly
|
|
|
|
# Blob storage. filesystem: a local dir shared by backend, consumer and every
|
|
# worker on the host. s3: any S3-compatible store, and the right answer once
|
|
# workers run off-host.
|
|
BLOB_PROVIDER=filesystem
|
|
BLOB_FS_ROOT=/data/blobs
|
|
# Derives from API_PUBLIC_URL, and so from PUBLIC_HOST, when unset. Setting it
|
|
# by hand opts it out of that.
|
|
# BLOB_PUBLIC_BASE_URL=http://localhost:8080/public
|
|
# BLOB_BUCKET=warmbly
|
|
# AWS_ENDPOINT_URL_S3=http://minio:9000
|
|
# AWS_REGION=us-east-1
|
|
# AWS_ACCESS_KEY_ID=
|
|
# AWS_SECRET_ACCESS_KEY=
|
|
|
|
# Delayed tasks (campaign ticks, scheduled sends). local is an in-process
|
|
# Postgres poller, so the backend must be running for scheduled work to fire.
|
|
TASKS_PROVIDER=local
|
|
# TASKS_LOCAL_POLL_INTERVAL=1s
|
|
|
|
# Billing. none unlocks every feature and never expires a trial.
|
|
BILLING_PROVIDER=none
|
|
# STRIPE_SECRET_KEY=
|
|
# STRIPE_WEBHOOK_SECRET=
|
|
# STRIPE_PUBLISHABLE_KEY=
|
|
|
|
# Where the backend and consumer read organization keys from. Workers use http.
|
|
# The shipped docker-compose.yml pins it per service, so this line only takes
|
|
# effect outside compose.
|
|
# ENCRYPTED_KEYS_PROVIDER=postgres
|
|
|
|
|
|
# === Mailbox connections ======================================================
|
|
#
|
|
# Needed on the backend AND every worker: the backend starts the OAuth flow,
|
|
# each worker refreshes the token. A worker missing these connects the mailbox
|
|
# fine and then stops silently about an hour later.
|
|
#
|
|
# Plain SMTP + IMAP mailboxes need none of this.
|
|
# Redirect URI: <API_PUBLIC_URL>/addresses/google/callback
|
|
# BOX_GOOGLE_CLIENT_ID=
|
|
# BOX_GOOGLE_CLIENT_SECRET=
|
|
# Redirect URI: <API_PUBLIC_URL>/addresses/outlook/callback
|
|
# BOX_OUTLOOK_CLIENT_ID=
|
|
# BOX_OUTLOOK_CLIENT_SECRET=
|
|
|
|
|
|
# === Worker (set in each worker's own environment) ============================
|
|
#
|
|
# Workers hold no database. They reach organization keys over the backend's
|
|
# internal API. An unset URL or token lets a worker start, subscribe and NEVER
|
|
# register, with no log line. WORKER_BIND_IP, WORKER_PUBLIC_IP and
|
|
# WORKER_INSTALLER_PATH are not forwarded by the shipped docker-compose.yml.
|
|
# ENCRYPTED_KEYS_PROVIDER=http
|
|
# ENCRYPTED_KEYS_BACKEND_URL=http://backend:8080
|
|
# ENCRYPTED_KEYS_WORKER_TOKEN= # same value as INTERNAL_API_TOKEN
|
|
#
|
|
# Stable identity. Set it only when you run one worker per host; scaled replicas
|
|
# share an environment and would collide.
|
|
# WORKER_ID=<uuid>
|
|
# WORKER_BIND_IP=
|
|
# WORKER_PUBLIC_IP=
|
|
# WORKER_TIER=free # free | premium | dedicated
|
|
# WORKER_EGRESS_KIND=
|
|
# MAIL_TLS_INSECURE=false # skips cert verification on mailbox connections
|
|
#
|
|
# Set on the BACKEND: the image the remote installer pulls. The built-in default
|
|
# does not match what CI publishes.
|
|
# WORKER_IMAGE=ghcr.io/<owner>/warmbly/worker:prod
|
|
# WORKER_INSTALLER_PATH=
|
|
|
|
|
|
# === Tracking service (Rust, open/click) ======================================
|
|
#
|
|
# The shipped docker-compose.yml pins the listen address, so these two only take
|
|
# effect outside compose.
|
|
# TRACKING_HOST=0.0.0.0
|
|
# TRACKING_PORT=3000
|
|
# Where tracking resolves opaque /c/<id> click tickets. Required: the service
|
|
# exits at boot without it, and without INTERNAL_API_TOKEN.
|
|
# BACKEND_INTERNAL_URL=http://backend:8080
|
|
# Counted pixel and click requests per source per minute. Over budget, pixels
|
|
# are still served but not counted and click redirects get 429.
|
|
# TRACKING_RATE_LIMIT_PER_MIN=300
|
|
# Read by the Rust publisher AND the Go subscriber. Override it in one place
|
|
# only and opens and clicks stop being consumed, with no error anywhere.
|
|
# KAFKA_TRACKING_TOPIC=tracking-events
|
|
|
|
|
|
# === Realtime service (Elixir/Phoenix) ========================================
|
|
#
|
|
# Read only when the release boots with MIX_ENV=prod, which is how the image
|
|
# runs. The shipped docker-compose.yml derives PORT, DATABASE_URL, REDIS_URL and
|
|
# JWT_SECRET from the values above, so those four only take effect outside
|
|
# compose. None of this block is on the admin panel's configuration page: that
|
|
# registry reads the backend process, not this container.
|
|
|
|
# The realtime service's own hostname. Derives from PUBLIC_HOST under compose.
|
|
# PHX_HOST=localhost
|
|
# PORT=4000
|
|
|
|
# MUST equal the backend's AUTH_SECRET or every websocket token is rejected and
|
|
# the dashboard loads but never goes live. Compose sets it from AUTH_SECRET.
|
|
# JWT_SECRET=
|
|
|
|
# DATABASE_URL=postgres://warmbly:warmbly@postgres:5432/warmbly_dev?sslmode=disable
|
|
# DATABASE_SSL=false
|
|
# REDIS_URL=redis://redis:6379
|
|
|
|
# false accepts a websocket upgrade from ANY origin, which is the shipped
|
|
# default. Set true once PHX_HOST is the public websocket hostname, so only that
|
|
# origin can open a socket.
|
|
# CHECK_ORIGIN=false
|
|
|
|
# Concurrent websocket connections. Per user is also capped by the caller's
|
|
# plan, whichever is lower.
|
|
# MAX_CONNECTIONS_PER_USER=10
|
|
# MAX_CONNECTIONS_PER_IP=50
|
|
# MAX_CONNECTIONS_GLOBAL=100000
|
|
|
|
# Websocket rate limits, per minute.
|
|
# RATE_LIMIT_WS_MESSAGE=120
|
|
# RATE_LIMIT_WS_JOIN=30
|
|
# RATE_LIMIT_WS_EVENT=60
|
|
|
|
# Realtime transport. false uses the Redis bridge and needs no cloud. Read
|
|
# identically by backend, consumer and realtime; never set true on one side.
|
|
PUBSUB_ENABLED=false
|
|
# GCP_PROJECT_ID= # required when PUBSUB_ENABLED=true
|
|
|
|
|
|
# === AI provider (optional) ===================================================
|
|
#
|
|
# Omit every AI variable to run with AI off: AI endpoints return a clean 503 and
|
|
# everything else works. Set these on the backend AND the consumer.
|
|
#
|
|
# An empty AI_PROVIDER with a set AI_API_KEY falls back to api.openai.com, which
|
|
# sends your key there. Set both or neither.
|
|
# AI_PROVIDER= # openai | openrouter | groq | ollama | anthropic | custom
|
|
# AI_API_KEY=
|
|
# AI_MODEL=
|
|
# AI_MODEL_TRIAL=
|
|
# AI_MODEL_PAID=
|
|
# AI_BASE_URL= # required for custom
|
|
# AI_FREE=
|
|
# SEARCH_PROVIDER= # serper | searxng
|
|
# SEARCH_API_URL=
|
|
# SEARCH_API_KEY=
|
|
|
|
|
|
# === Integrations (optional) ==================================================
|
|
# Shared redirect URI for the CRM and messaging OAuth flows. Unset, it becomes
|
|
# BACKEND_PUBLIC_URL + /integrations/oauth/callback, and http://localhost:8080
|
|
# when that is unset too. Not forwarded by the shipped docker-compose.yml.
|
|
# INTEGRATIONS_OAUTH_REDIRECT_URL=
|
|
# BACKEND_PUBLIC_URL=https://api.example.com
|
|
|
|
|
|
# === Observability and push (optional) ========================================
|
|
# Optional in EVERY environment, including APP_ENV=prod.
|
|
# SENTRY_DSN=
|
|
#
|
|
# Mobile push. Partial configuration disables push with a warning, never a
|
|
# crash. Set on the backend and the consumer. Not forwarded by the shipped
|
|
# docker-compose.yml.
|
|
# APNS_KEY=
|
|
# APNS_KEY_PATH=
|
|
# APNS_KEY_ID=
|
|
# APNS_TEAM_ID=
|
|
# APNS_TOPIC=
|