Configuration
Environment variables for the control-plane, edge, and console, plus production guardrails.
Naming: the rebrand fallback
Control-plane variables are read CUSTOMDOMAIN_* first, then JUSTEASY_* as
a fallback, so deployments from before the rename keep working. Either prefix
works; CUSTOMDOMAIN_* wins if both are set. The tables below list the canonical
JUSTEASY_* name.
Control-plane
| Var | Purpose | Default |
|---|---|---|
JUSTEASY_ADDR | HTTP listen address | :8080 |
JUSTEASY_ENV | production enables guardrails | development |
JUSTEASY_DATABASE_URL | Postgres DSN (in-memory store if unset) | unset (required in prod) |
JUSTEASY_TOKEN_TTL | widget-JWT lifetime | 60m |
JUSTEASY_MANAGEMENT_KEY | gates provisioning + cross-tenant reads | unset (required in prod) |
JUSTEASY_PROVISION_SECRET | gates POST /v1/tenants:provision | unset |
JUSTEASY_EDGE_CNAME_TARGET | host connected domains point at | edge.customdomain.ai |
EDGE_ASK_SECRET | shared secret for /internal/ask (X-Edge-Auth) | unset (ask disabled) |
SELL_PURCHASE_ENABLED | 1/true allows registrar purchases (fail-closed) | off |
SECURE_ENABLED | 1/true enables the Secure (/ssl certificate) surface; otherwise every /ssl route reports not-configured | off |
POWER_ENABLED | 1/true enables the Power (/power edge-origin) surface; otherwise every /power route reports not-configured | off |
MONITOR_ALERTS_ENABLED | 1/true lets the hourly drift sweeper deliver domain.record_missing/_restored webhooks (it still runs in shadow mode when off) | off |
OAUTH_STATE_KEY | HMAC key persisting OAuth state across restarts | boot-time key |
OAUTH_ALLOWED_RETURN_ORIGINS | comma-separated postMessage allowlist | unset |
OAUTH_<PROVIDER>_CLIENT_ID / _CLIENT_SECRET | per-provider OAuth app (Rail C) | unset |
DC_SIGNING_KEY_FILE / DC_SIGNING_KEY_SELECTOR | RS256 key + DNS selector for Domain Connect apply URLs | unset |
STRIPE_SECRET_KEY | enables billing checkout/portal | unset (catalog still served) |
STRIPE_PRICE_STARTUP / STRIPE_PRICE_GROWTH / STRIPE_PRICE_GROWTH_METERED | Stripe price ids | unset |
NAMECOM_USERNAME / NAMECOM_TOKEN | registrar (Sell) credentials | unset |
SCIM_BEARER_TOKEN / SCIM_BASE_URL | enterprise SCIM 2.0 provisioning | unset |
The propagation poller runs automatically (1-minute interval) — there is no separate enable flag.
Edge
| Var | Purpose | Default |
|---|---|---|
--addr (flag) | HTTPS listen address | :8443 |
EDGE_ASK_URL | control-plane origin for the fail-closed gate (POST {EDGE_ASK_URL}/internal/ask); set ⇒ production | unset (dev/mock gate) |
EDGE_ASK_SECRET | shared secret sent as X-Edge-Auth | unset |
EDGE_ACME_PROD | 1 uses the Let's Encrypt production directory (else staging) | unset |
EDGE_ACME_EMAIL | ACME account contact | "" |
EDGE_CERT_CACHE | autocert cert/account cache dir | /data/certs |
EDGE_HTTP_ADDR | :80 listener for ACME HTTP-01 | :80 |
In dev (neither EDGE_ASK_URL nor EDGE_ACME_PROD set) the edge issues
self-signed certs and uses a mock gate, so go test and local runs need no
network. It refuses to run EDGE_ACME_PROD with an empty EDGE_ASK_URL (an
ungated issuer).
Console (apps/app)
| Var | Purpose |
|---|---|
CP_BASE_URL | control-plane base the server proxy calls |
WEB_SECRET_KEY | seals each tenant's control-plane API key at rest (AES-256-GCM) |
BETTER_AUTH_SECRET | session-signing secret — required in production (the app fails closed if unset) |
BETTER_AUTH_URL / NEXT_PUBLIC_APP_URL | canonical app URL for auth callbacks |
DATABASE_URL | Postgres for the console's own tables |
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | optional social sign-in |
JUSTEASY_PROVISION_SECRET | the provision secret the console sends to tenants:provision (CD_PROVISION_SECRET also accepted) |
Production guardrails
Setting JUSTEASY_ENV=production makes the control-plane refuse to start on
unsafe configuration:
JUSTEASY_DATABASE_URLunset (or unreachable) → refuse to start.JUSTEASY_MANAGEMENT_KEYunset → refuse to start (provisioning would be open).- The edge refuses
EDGE_ACME_PRODwithoutEDGE_ASK_URL.
Health
GET /healthz is the liveness + database-readiness probe (no tenant data, no
secrets). The edge gate POST /internal/ask is the only control-plane call the
edge makes and must be network-isolated to the edge.