Configuration reference

August 19, 2026 · View on GitHub

Every setting Hexis reads, and where it comes from.

Four values are required before first boot. Everything marked setup screen can be left unset and configured in the app at first sign-in. Anything set in the environment wins over the setup screen, so a value you pin in .env cannot be changed out from under you in the UI.

.env.example documents every variable in full.

Variables

VariableRequiredPurpose
ADMIN_EMAILyesDeployment owner: always an admin, and the initial Admin of a freshly seeded KB
ADMIN_PASSWORDwith password loginBootstrap sign-in password, checked against the env and never stored. Not needed when LOGIN_PASSWORD=false
JWT_SECRETyesSigns login sessions + OAuth state
SECRETS_ENC_KEYyes32-byte key (base64/hex) encrypting vault secrets + MCP OAuth tokens
DATABASE_URLsee notePostgres connection string. Unset under compose, the app builds it from the POSTGRES_* values the bundled db was created with
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DBnoCredentials for the bundled database (applied only when its volume is first created)
KB_REPO_URLsetup screenhttps clone/push URL of the knowledge-base repo, on any git host
GIT_TOKEN / GIT_USERNAMEsetup screenGit credential (HTTP Basic password / host-specific username; see .env.example for per-host usernames)
DEFAULT_BRANCH / PROTECTED_BRANCHESsetup screenBranch model. Runtime-only: served to the frontend over /api/config, so one build runs anywhere
DOMAINwith the https profilePublic host name served by the bundled Caddy; also derives the public origins (https://<DOMAIN>) and TRUST_PROXY=1 unless set explicitly
PUBLIC_BACKEND_URL / PUBLIC_FRONTEND_URLproductionPublic origins for OAuth redirects + post-login bounces (derived from DOMAIN when set)
TRUST_PROXYbehind a proxyReverse-proxy hop count, so req.ip and the login rate limit see the real client (defaults to 1 when DOMAIN is set)
OIDC_ISSUER_URL / OIDC_CLIENT_ID / OIDC_CLIENT_SECRETnoGeneric OIDC SSO; the login method appears once all three are set
ALLOWED_EMAIL_DOMAINSwith multi-tenant SSOSignup allow-list for SSO auto-provisioning
LOGIN_PASSWORDnofalse hides password login and rejects the endpoint
PORTnoBackend port (default 3001)
KB_DIR_NAMEnoDirectory name of the KB clone inside each workspace
TENANT_IDnoSlug branding credential prefixes (default bevel)
KB_TEMPLATE_DIRnoOverrides the packaged KB seed template
ONTOLOGY_SESSION_BLOCKnoOntology-session touch tracking toggle (default on)

Generating the two secrets

JWT_SECRET and SECRETS_ENC_KEY are both 32 random bytes. Run this twice and paste one result into each. Never reuse the same value for both:

node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
# no Node installed? docker run --rm node:22-slim node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

Rotating SECRETS_ENC_KEY makes every stored vault secret and MCP OAuth token undecryptable, so treat it as permanent for the life of the deployment.

Single sign-on

Set OIDC_ISSUER_URL, OIDC_CLIENT_ID and OIDC_CLIENT_SECRET for any spec-compliant provider, or configure it on the setup screen, which shows you the redirect URI to register with your identity provider.

  • SSO-only deployments: set LOGIN_PASSWORD=false and drop ADMIN_PASSWORD. The password endpoint is then rejected, not merely hidden.
  • Multi-tenant issuers (Google, Entra common): set ALLOWED_EMAIL_DOMAINS. SSO auto-provisions accounts, and that list is the only signup boundary. Without it, anyone with an account at the issuer can sign in.

Configuring by environment instead of the setup screen

Every value the setup screen collects has an env var (KB_REPO_URL, GIT_TOKEN, DEFAULT_BRANCH, …). Setting them in the environment skips those steps at first sign-in and pins them against later change in the UI, which is what you want for a deployment managed by config-as-code.

State that survives redeploys

Postgres data plus three app volumes (workspace clones, diff-review backups, tool-chain spill files) are named volumes, so a redeploy or image rebuild loses nothing.

Back up the pgdata volume and your knowledge-base git repository. Everything else is derivable from those two.

Health

GET /api/health. First boot can take a minute or two while it runs migrations and seeds the knowledge-base repo, so give the container its start_period (~90s) before treating an unhealthy status as a fault.

Migrations run automatically on boot; there is no separate migrate step, in development or in production.


Stuck on something specific? See troubleshooting.