All 103 environment variables Veefore reads, with their defaults.
Generated page. Built from .env.example. Edit the source, not this page.
Veefore is configured entirely through the environment. There is no configuration file the services read at runtime and no settings table that can disagree with the process that booted. Configuration is validated at startup; an invalid value stops the process rather than degrading it.
Secrets belong in a secret manager
The cryptographic keys below are the ones that matter: data encryption, platform signing, session signing and the audit chain. In anything beyond local development, inject them from a secret manager. Rotating the signing key invalidates issued passports; rotating the data encryption key requires re-sealing credential material.
Copy .env.example to .env to start. npm run dev reads the root .env, and so does the console’s Vite config, so the ports stay consistent across every process.
The Ships as column is the value in .env.example, which is also the value the services fall back to when the variable is unset. Where the example file carries a commented-out alternative, it is shown underneath.
Platforms that assign a port (Railway, Heroku, Cloud Run) pass it as PORT. Leave CONTROL_PLANE_PORT and GATEWAY_PORT unset there and the service binds PORT; set either one and it wins, which is what keeps local development on 4000 and 4010 regardless of what the shell exports. Alternative: PORT= Comma-separated list of browser origins allowed to call the control plane. Production: https://console.veefore.com,https://veefore.com Mostly a backstop: the console reaches /v1 same-origin (Vite's proxy in development, an edge rewrite in production), so the browser sends no Origin header and CORS never engages. The list is also what social sign-in matches a forwarded host against when choosing a redirect base.
CONSOLE_PUBLIC_URL
empty
Where people open the console. The CLI sends engineers here to sign in from the browser. Empty means the first CORS_ALLOWED_ORIGINS entry. Production: https://console.veefore.com
MARKETING_PUBLIC_URL
empty
Where the public marketing site is served. Separate deployment, separate origin (apps/marketing). Production: https://veefore.com
SESSION_HINT_COOKIE_DOMAIN
empty
Domain for veefore_signed_in — a non-secret cookie holding the single character 1, which is how the marketing site on the apex domain knows to hand a signed-in visitor over to the console. Nothing on the server reads it and forging it grants nothing. The session cookie itself is NEVER domain-scoped: it stays HttpOnly and host-only on the console's origin. See apps/control-plane/src/http/session-cookies.ts. Empty keeps the hint host-only too, which is right for local development and for any deployment serving the site and the console from one host. Set it to the registrable domain they share when they are on sibling hosts. Production: .veefore.com
VEEFORE_HOSTED
false
true only on the hosted Veefore service that the published CLI connects to by default. Self-hosted installs leave it false, and setup instructions then show engineers this deployment's address to pass to veefore --control-plane once.
When DATABASE_URL is set, the pg driver is used (production path). When it is empty, Veefore boots an embedded PGlite Postgres, which is per-process: the control plane, the gateway and the worker each get their own private copy and see none of each other's writes. Correct for isolated tests, wrong for running the stack. Set this for anything multi-process. Alternative: postgres://veefore:veefore@localhost:5432/veefore
DATABASE_SSL
false
DATABASE_POOL_MAX
20
DATABASE_STATEMENT_TIMEOUT_MS
15000
PGLITE_DATA_DIR
memory://
Directory used by the embedded PGlite instance. memory:// = ephemeral.
DATABASE_ENABLE_RLS
false
Enable PostgreSQL row level security policies (requires the pg driver).
32-byte base64 key used to envelope-encrypt sensitive metadata at rest. Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
VEEFORE_SIGNING_PRIVATE_KEY
empty
Base64 PKCS8 Ed25519 private key used to sign agent identity tokens and passports. Generate with: npm run -s migrate -- --print-keypair
VEEFORE_SIGNING_KEY_ID
empty
VEEFORE_SESSION_SECRET
empty
Secret used to sign human browser sessions.
VEEFORE_AUDIT_CHAIN_SECRET
empty
Audit hash-chain pepper. Rotating this breaks historical verification.
invite_only (default): a new organization needs an invitation, issued when a platform operator approves a waitlist application (/waitlist). The link is e-mailed, works for one sign-up with the applicant's address, and expires 24 hours after it is sent. open lets anyone sign up.
PLATFORM_SUPERADMIN_EMAIL
empty
Platform admin (admin.veefore.com). Admin accounts are separate from customer accounts: they sign in only on the admin site, always with a password and an authenticator code, and can never sign in to the console. The superadmin below is created on start and kept in step with these values, which is how a new deployment gets its first way in. Only the password HASH goes here. Generate one (and a strong password) with: npm run admin:password Changing the hash signs the superadmin out everywhere. Further admins are added by a superadmin in the admin site, under Admins.
Admin sessions: absolute lifetime and idle timeout, in seconds.
PLATFORM_ADMIN_IDLE_TIMEOUT_SECONDS
1800
PLATFORM_ADMIN_ALLOWED_IPS
empty
Optional comma-separated IPs or CIDRs the platform admin accepts requests from (e.g. 203.0.113.7,10.0.0.0/8). Empty means any network. Only set this when your edge proxy overwrites X-Forwarded-For with the real client address.
#Social sign-in (Google, GitHub). A provider appears on the sign-in page only when both its client id and secret are set. Register this redirect URI with the provider, exactly: <OAUTH_REDIRECT_BASE_URL>/v1/auth/oauth/google/callback <OAUTH_REDIRECT_BASE_URL>/v1/auth/oauth/github/callback The base is the browser-facing origin that serves /v1 (normally the console, which proxies it). Empty means CONSOLE_PUBLIC_URL. Must be https in production. Keep the secrets in your secret manager, not in this file.
Variable
Ships as
Notes
OAUTH_REDIRECT_BASE_URL
empty
OAUTH_GOOGLE_CLIENT_ID
REPLACE_WITH_GOOGLE_CLIENT_ID
OAUTH_GOOGLE_CLIENT_SECRET
REPLACE_WITH_GOOGLE_CLIENT_SECRET
OAUTH_GITHUB_CLIENT_ID
REPLACE_WITH_GITHUB_CLIENT_ID
OAUTH_GITHUB_CLIENT_SECRET
REPLACE_WITH_GITHUB_CLIENT_SECRET
OAUTH_HTTP_TIMEOUT_MS
10000
#Passkeys and security keys (WebAuthn) Passkeys are bound to WEBAUTHN_RP_ID (a registrable domain such as veefore.example.com). Changing it later invalidates every registered passkey, so set it once. Defaults: the hostname of CONSOLE_PUBLIC_URL, and that URL plus CORS_ALLOWED_ORIGINS as the accepted origins. Browsers only allow passkeys on https origins and on http://localhost.
resend | log Every e-mail (verification codes, password resets and security notices, waitlist and beta invitations, team invitations, alerts) is sent through the Resend API with EMAIL_TRANSPORT=resend. log never sends anything: outside staging/production it keeps the last messages for GET /v1/dev/outbox (local machine only); in staging/production it is refused.
Must be an address on a domain verified in Resend (SPF + DKIM).
EMAIL_FROM_NAME
Veefore
EMAIL_SUPPORT_ADDRESS
empty
Shown in e-mail footers and used as Reply-To unless EMAIL_REPLY_TO is set.
EMAIL_REPLY_TO
empty
EMAIL_COMPANY_ADDRESS
empty
Postal address in the footer of every e-mail. Empty hides the line.
EMAIL_LOGO_URL
empty
Logo in the e-mail header, as an absolute http(s) URL of a PNG or JPG (SVG is not supported by most mail clients). Empty serves the bundled mark from the console at <CONSOLE_PUBLIC_URL>/brand/veefore-mark.png, which a mail client can only load once the console is reachable publicly.
VITE_TERMS_URL
empty
Console legal links shown on the sign-in and sign-up pages. Absolute https:// URLs or same-origin paths. Empty hides the link.
#Front ends (Vite — only VITE_* vars reach the browser; never put secrets here)
Variable
Ships as
Notes
VITE_CONSOLE_URL
http://localhost:5173
VITE_* values are compiled into the bundle at build time. Changing one needs a rebuild and redeploy; restarting a server does nothing. There is deliberately no API base URL. The console calls /v1 and /scim as same-origin paths, proxied to the control plane by the edge (Vite in development, a Vercel rewrite in production). That is what keeps the session cookie host-only and SameSite=Lax rather than a cross-site cookie, and keeps CORS out of the browser's path. Adding one would undo all of it. Where the marketing site sends people to sign in, and where the console's "home" link goes. The two apps run on different ports locally and different origins in production. Production: https://console.veefore.com and https://veefore.com
VITE_MARKETING_URL
http://localhost:5174
VITE_DOCS_URL
http://localhost:4800
Where the marketing site's "Docs" links go. The docs are their own static site on their own origin, not a path on the marketing site. Production: https://docs.veefore.com (veefore.com/docs redirects there)
VEEFORE_CONSOLE_PORT
5173
Dev-server ports. Bound with strictPort, so a collision is a startup error rather than a silent move to another port. Change CORS_ALLOWED_ORIGINS to match the console's, or the browser refuses every request with no useful error.
VEEFORE_CONSOLE_PREVIEW_PORT
4173
VEEFORE_MARKETING_PORT
5174
VEEFORE_MARKETING_PREVIEW_PORT
4174
VEEFORE_ADMIN_PORT
5175
VEEFORE_ADMIN_PREVIEW_PORT
4175
VEEFORE_CONTROL_PLANE_URL
empty
Proxy target for the dev server. Defaults to CONTROL_PLANE_PORT on loopback.
Serves docs/dist on the loopback interface only. It is unauthenticated content from a working tree and has no business being reachable remotely.
#Example downstream service (examples/real-tool-server.mjs)
Variable
Ships as
Notes
TOOL_SERVER_PORT
4500
A real HTTP service on loopback with its own API key, used to observe what actually reaches a downstream under each enforcement mode. The key belongs to the credential broker, never to the agent.
TOOL_SERVER_API_KEY
downstream-secret-key
#Veefore-hosted AI code assist (veefore integrate --assist=veefore)
Variable
Ships as
Notes
VEEFORE_AI_PROVIDER
empty
Off unless a provider and key are set. Developers can always use their own key (--assist=own) or make the change by hand (--assist=manual) instead. Source sent here is used for the request only; it is never stored or logged.
VEEFORE_AI_API_KEY
empty
VEEFORE_AI_MODELS
empty
Comma-separated model ids developers may pick from, e.g. the ids your provider's /v1/models lists.
Off unless a URL is set; admins can always enter manual rates in Admin › Settings, and a manual rate always overrides an automatic one for the same pair. Veefore sends one GET to this URL per refresh (daily, and on demand) and nothing else: no tenant data is ever included. The response must be {"base":"USD","date":"YYYY-MM-DD","rates":{"INR":83.1,"EUR":0.92}} which is what frankfurter.app and exchangerate.host return, e.g. Alternative: VEEFORE_FX_PROVIDER_URL=https://api.frankfurter.app/latest?from=USD