Skip to main content

Configuration

All configuration is environment variables. The app validates its environment both at build time and on start, so every variable declared in src/env.ts must be present; the image sets safe non-secret defaults for all of them. Override the ones you care about.

With compose, set variables in .env next to docker-compose.yml. Only the variables listed in the compose x-app-env/build anchors reach the containers.

Required in production​

VariablePurpose
DATABASE_URLPostgres connection string, e.g. postgres://user:pass@host:5432/intranet.
BETTER_AUTH_SECRETSession/token signing secret. 32+ characters, high entropy.
ORIGINOptional public base URL (scheme + host + port). See Origin.
APP_ENVdevelopment, staging, or production.
OAUTH_*Provider connection. See Single sign-on.

Origin and CSRF​

ORIGIN is optional. When it is unset, the app derives the origin per request from the Host header; adapter-node defaults the protocol to https, and behind a proxy you can set PROTOCOL_HEADER/HOST_HEADER (e.g. x-forwarded-proto/x-forwarded-host) so the derived origin matches the public URL. Because the image no longer bakes the origin, one image can be deployed to any domain.

SvelteKit's cross-origin form check is disabled (csrf.trustedOrigins: ['*']), and better-auth resolves its base URL from the request (baseURL: { allowedHosts: ['*'] }). Authentication is still enforced server-side.

Consequences:

  • Local compose works out of the box with no ORIGIN set.

  • To pin a canonical origin (for example when serving over plain HTTP, where the derived protocol would default to https), set ORIGIN:

    # plain Docker
    docker run -e ORIGIN=https://intranet.example ...

    # compose, via .env
    PUBLIC_ORIGIN=https://intranet.example
  • OIDC redirect URIs still default to the browser origin; if you set ORIGIN, register that exact origin with the provider.

Variables​

Core​

VariableDefault (image)Notes
APP_ENVdevelopmentEnables staging guard when staging.
DATABASE_URLpostgres://intranet:intranet@db:5432/intranetOverride in production.
ORIGIN(unset; derived from Host)Optional. Pin a canonical origin if needed.
BETTER_AUTH_SECRETdev-only-change-me-…Override.
PORT / HOST3000 / 0.0.0.0Listen address.
TEST_DATABASE_URL…@db:5432/intranet_testMust be present; used by integration tests.

Sign-in (provider-agnostic)​

See Single sign-on with Authentik for a worked example.

VariableDefaultNotes
OAUTH_PROVIDER_IDssoKey in the callback path: /api/auth/callback/<id>.
OAUTH_PROVIDER_NAMESingle sign-onLogin button label.
OAUTH_DISCOVERY_URL(empty)OIDC discovery document; preferred.
OAUTH_AUTHORIZATION_URL(empty)Use instead of discovery for plain OAuth2.
OAUTH_TOKEN_URL(empty)Plain OAuth2 token endpoint.
OAUTH_USERINFO_URL(empty)Optional for plain OAuth2.
OAUTH_CLIENT_ID(empty)
OAUTH_CLIENT_SECRET(empty)
OAUTH_SCOPESopenid profile email
OAUTH_PKCEtrue
OAUTH_GROUPS_CLAIMgroupsClaim carrying group membership.
OAUTH_ROLE_MAP(empty)JSON: provider group → app role.
OAUTH_DEFAULT_ROLEengineerRole for unmapped identities.
OAUTH_EXTRA_PROVIDERS(empty)JSON array of additional providers.
warning

If OAUTH_ROLE_MAP or OAUTH_GROUPS_CLAIM never reach the container, every identity silently becomes OAUTH_DEFAULT_ROLE (engineer). Verify after deploying by signing in and checking the role shown in the sidebar.

Staging isolation​

APP_ENV=staging refuses production endpoints and restricts access to an allowlist.

VariableNotes
STAGING_ALLOWED_EMAILSComma-separated identities allowed on staging.
PRODUCTION_DATABASE_URLProduction DB, for the isolation check.
PRODUCTION_OAUTH_ISSUERProduction OAuth issuer, for the isolation check.

Accounts and provisioning​

There are no local passwords. On first provider login the app creates a local user and sets the role from the group claim; every later login re-syncs it. The /dev/login route mints a session directly and is handy for tests, but production users sign in through the provider.

API credentials​

The HTTP API uses two credential types, created in the admin UI:

  • Personal tokens (cxp_…) inherit the owner's role and scopes.
  • Service clients (cxs_… + secret) use OAuth 2.0 client credentials for machine-to-machine accounting access.

See the API reference linked in the navbar.