Skip to content

Configuration

All configuration is via environment variables. Copy .env.example to .env; the defaults match infra/docker-compose.dev.yml.

VarDefaultNotes
DATABASE_URLpostgresql://memoturn:memoturn@localhost:5433/memoturn?schema=publicHost port is 5433 in dev to avoid clashing with other local Postgres
VarDefaultNotes
TELEMETRY_ENGINEdorisdoris (the scale engine, default) or postgres — the small-install tier that keeps telemetry in the DATABASE_URL Postgres, schema telemetry, with no Doris containers at all. Needs a pgvector-enabled image (the shipped compose files use pgvector/pgvector:pg16). Sizing + how to switch engines: Deployment → Telemetry engine.
TELEMETRY_DATABASE_URLDATABASE_URLOptional separate Postgres for the postgres engine’s telemetry tables.
TELEMETRY_PG_SCHEMAtelemetrySchema holding the postgres engine’s tables + migration ledger.

Applies when TELEMETRY_ENGINE=doris (the default).

VarDefaultNotes
DORIS_HOSTlocalhostDoris FE host
DORIS_PORT9030FE MySQL-protocol port
DORIS_HTTP_PORT8030FE HTTP port
DORIS_USERroot
DORIS_PASSWORDemptyEmpty in dev; required in production (the prod compose files include a one-shot doris-setup service that sets the root password)
DORIS_DBmemoturn
DORIS_FE_XMX4096mFE JVM heap cap, applied by the prod compose file. Raise on hosts with memory to spare (see Deployment → Doris sizing).
DORIS_BE_MEM_LIMIT6G dev / 40% prodBE process memory cap (absolute like 8G, or a % of host memory). Raise if analytical queries fail with MEM_ALLOC_FAILED.
TELEMETRY_STREAM_LOADfalseSet true/1 to switch worker inserts to Doris Stream Load (HTTP) for higher throughput. In-network deploys just work (the FE 307-redirects to a BE).
DORIS_STREAM_LOAD_HOSTDORIS_HOSTOverride for a host-run worker that should load a BE directly instead of going through the FE redirect.
DORIS_STREAM_LOAD_PORT8030FE HTTP port by default; point at a BE webserver (8040) to load it directly.
DORIS_STREAM_LOAD_TIMEOUT_MS60000Per-call Stream Load timeout so a wedged BE can’t pin an ingest worker slot forever.
VarDefaultNotes
REDIS_URLredis://localhost:6380Host port 6380 in dev
VarDefaultNotes
WORKER_CONCURRENCY10Ingest worker concurrency
WORKER_PORT3002Worker /health + /metrics HTTP endpoint
WORKER_HOST127.0.0.1Bind host for the worker health/metrics server. Loopback by default — /metrics is unauthenticated and leaks queue depths and per-project evaluator names. Set 0.0.0.0 only for cross-host probes on a trusted network.
WORKER_METRICS_URLhttp://127.0.0.1:3002/metricsWhere the API fetches worker metrics for the ingest-health panel. Set it when the API and worker run on different hosts/pods; fetch failures degrade gracefully (workerReachable: false).
STATE_RETENTION_HOURS72Hours a mutable-entity *State row stays in Postgres after its last update before the hourly prune drops it (Doris keeps full history).
EXPERIMENT_CONCURRENCY2Concurrent experiment jobs per worker process. Kept low on purpose — each job fans out over dataset items and must not starve ingest.
EXPERIMENT_ITEM_CONCURRENCY4Dataset items executed in parallel within one experiment run.
MAINTENANCE_CONCURRENCY4Maintenance-queue concurrency, so the per-minute alert tick isn’t blocked behind a long daily sweep (retention/export/embeddings). Each job type is lock-guarded.
GUARDRAIL_EVALUATOR_TIMEOUT_MS3000Per-evaluator timeout for synchronous guardrail checks (they sit on the request path, so a slow LLM judge must not hang the caller).
ALERT_ANOMALY_BUCKETS12Number of trailing time buckets used as the baseline for anomaly-type alert rules.
ALERT_ANOMALY_MIN_BASELINE5Minimum baseline events before an anomaly rule can fire (suppresses noise on quiet projects).
EMBEDDING_PROJECTION_DAYS30Lookback window for the daily embedding-projection reduction.
EMBEDDING_PROJECTION_MAX_POINTS5000Cap on points per projection run.
EMBEDDING_PROJECTION_CLUSTERS8k-means cluster count for the projection.
SIMILAR_TRACES_SEED_CAP8Max seed vectors compared per “find similar traces” query (each adds a distance term to the Doris SQL).
VarDefault
BLOB_ENDPOINThttp://localhost:9000
BLOB_REGIONus-east-1
BLOB_BUCKETmemoturn
BLOB_ACCESS_KEY_IDmemoturn
BLOB_SECRET_ACCESS_KEYmemoturn123
BLOB_FORCE_PATH_STYLEtrue
VarDefaultNotes
API_PORT3001Hono API
CONSOLE_PORT3000Vite SPA
MEMOTURN_API_URLhttp://localhost:3001API target for the console dev proxy
RATE_LIMIT_PER_MINUTE0Per-project global request rate limit (requests/minute); 0 disables it (per-key limits still apply)
INGEST_EVENTS_PER_MINUTE0Per-project ingest event-rate budget (events/minute; 0 = disabled). Meters actual event volume — a single POST can carry up to 1000 events, so this catches burst loads that the request-count limit would miss. Returns 429 with Retry-After when exceeded.
MCP_RATE_LIMIT_PER_MINUTE120Per-IP budget for the remote MCP endpoint (/v1/mcp/:projectId). Unlike the project limiter it defaults on — the route runs a credential lookup before auth resolves, so unauthenticated clients must not get unthrottled tries. 0 disables.
RATE_LIMIT_TRUSTED_PROXIES1Number of trusted reverse proxies in front of the API, used to derive the real client IP from the right of X-Forwarded-For (a spoofed XFF prefix can’t evade per-IP limits). The shipped Caddy deploy is one proxy; set 0 if the API is directly internet-exposed.
API_METRICS_TOKENunsetEnables GET /metrics (request counts, status classes, per-route latency percentiles). Unset → 404; set → requires Authorization: Bearer <token>.

Production startup guard: in production (NODE_ENV=production) the API and worker refuse to start if BETTER_AUTH_SECRET, ENCRYPTION_KEY, or AUTH_TRUSTED_ORIGINS are missing, shorter than 16 characters, or set to a known development placeholder. Generate fresh values with openssl rand -base64 48.

VarDefaultNotes
BETTER_AUTH_SECRETdev placeholderRequired in production — signs session cookies and tokens. Use openssl rand -base64 48.
AUTH_BASE_URLhttp://localhost:3001Better Auth base URL
AUTH_TRUSTED_ORIGINShttp://localhost:3000Required in production — comma-separated console origins for CORS + auth.
ENCRYPTION_KEYdev placeholderRequired in production — AES-256-GCM key for provider API keys stored at rest. Independent of BETTER_AUTH_SECRET. Rotating this invalidates all stored provider keys (they must be re-entered in Settings → Providers).
MCP_LOGIN_PAGE<first AUTH_TRUSTED_ORIGINS>/loginConsole sign-in page the remote-MCP OAuth 2.1 flow (Better Auth @better-auth/oauth-provider plugin) redirects unauthenticated users to. Override only if the console login lives elsewhere.
MCP_CONSENT_PAGE<first AUTH_TRUSTED_ORIGINS>/consentConsole consent page where the OAuth flow asks the signed-in user to approve the client’s requested scopes.
VarDefaultNotes
AUTH_DISABLE_PASSWORD_SIGNUPunsetSet true to disable new email/password signups (existing password logins still work). Meant for hosted/IdP-only deployments; leave unset for self-host so the first admin can register without SMTP or an IdP.
AUTH_REQUIRE_EMAIL_VERIFICATIONunsetSet true to require a verified email before sign-in (needs a working email transport). Default off so self-host accounts aren’t locked out.
AUTH_MIN_PASSWORD_LENGTH12Minimum length for new passwords (existing shorter passwords still sign in).
AUTH_HIBP_DISABLEDunsetThe breached-password check (k-anonymity, api.pwnedpasswords.com) is on by default and fails closed — signup/password-change return 500 when the service is unreachable. Airgapped/offline installs must set true.
AUTH_COOKIE_CACHE_MAX_AGE300Session cookie cache lifetime (seconds) — getSession is served from a short-lived signed cookie instead of a Postgres query. Revocations/bans take up to this long to bite on issued cookies.
AUTH_COOKIE_CACHE_DISABLEDunsetSet true to disable the session cookie cache entirely (every getSession hits Postgres).
AUTH_ORG_MEMBERSHIP_LIMIT10000Max members per organization.
AUTH_ORG_INVITATION_LIMIT1000Max pending invitations per organization.
AUTH_IP_HEADERSunsetComma-separated header(s) carrying the real client IP behind a proxy/CDN (first match wins), e.g. cf-connecting-ip or x-real-ip. Unset, the auth rate limiter trusts x-forwarded-for, which is spoofable when clients can reach the origin directly.
SSO_ADMIN_GROUPSemptyComma-separated IdP groups/roles that map a federated user to org admin on auto-join (matched against the groups/roles/role claims). Empty → everyone joins as member.
SUPERADMIN_USER_IDSemptyComma-separated user IDs that always pass platform-admin authorization (list/ban users, impersonate), independent of the user.role column. Keep empty on self-host unless you need it.
PASSKEY_RP_IDderivedWebAuthn relying-party ID (registrable domain, no scheme/port). Defaults derive from the first AUTH_TRUSTED_ORIGINS; set only when the console is served from a custom domain.
PASSKEY_ORIGINderivedFull origin users register passkeys from. Must match the console origin.
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRETunsetEnable Google sign-in (button appears only when both are set). Callback: ${AUTH_BASE_URL}/auth/callback/google.
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRETunsetEnable GitHub sign-in. Callback: ${AUTH_BASE_URL}/auth/callback/github.

Powers the email alert channel and auth flows (password reset, org invitations, email verification). The transport is auto-selected: an explicit EMAIL_TRANSPORT, else Resend when RESEND_API_KEY is set, else SMTP when SMTP_CONNECTION_URL or SMTP_HOST is set, else disabled. When disabled, auth emails are logged to stderr in development so flows stay testable without a mail server.

VarDefaultNotes
EMAIL_TRANSPORTautoForce resend or smtp instead of auto-selection.
EMAIL_FROMmemoturn <alerts@memoturn.local>From address for all outbound mail.
ALERT_EMAIL_FROMunsetLegacy fallback for the From address — used only when EMAIL_FROM is unset. Prefer EMAIL_FROM.
RESEND_API_KEYunsetResend HTTP API key (no SMTP server needed).
SMTP_CONNECTION_URLunsetSingle connection URL (smtp:// / smtps:// / ses://<region>); wins over the discrete vars.
SMTP_HOST / SMTP_PORT / SMTP_SECURE / SMTP_USER / SMTP_PASSunsetDiscrete SMTP settings (used when no connection URL is set).
VarDefaultNotes
LLM_TIMEOUT_MS60000Wall-clock timeout for non-streaming provider calls (evaluator/experiment judges — a hung provider would otherwise wedge a shared worker slot).
LLM_STREAM_TIMEOUT_MS300000Timeout for streaming calls (playground).
VarDefaultNotes
ALLOW_PRIVATE_WEBHOOK_TARGETSunsetSet to 1 to permit http:// and private/loopback webhook, automation, and analytics-sink URLs. Blocked by default in every environment to prevent SSRF (not just production). Useful for dev/LAN self-hosted targets.
VarDefaultNotes
MEMOTURN_BASE_URLhttp://localhost:3001API base used by SDKs
MEMOTURN_PUBLIC_KEYpk-mt-devMatches the dev key from bun run seed
MEMOTURN_SECRET_KEYsk-mt-dev

bun run seed creates the default organization, project, API key, and admin user. In development the credentials are the well-known dev defaults; in production the script refuses to run unless ALLOW_SEED=1 is set, at which point it generates random credentials and prints them once.

VarDefaultNotes
ALLOW_SEEDunsetSet to 1 to allow bun run seed in NODE_ENV=production. Without it the script exits with an error (the dev credentials are public knowledge).
SEED_ADMIN_EMAILadmin@memoturn.devOverride the seeded admin email. In production a random value is generated unless this is set.
SEED_ADMIN_PASSWORDmemoturn-dev-123Override the seeded admin password. In production a random value is generated unless this is set.
VarDefaultNotes
CONSOLE_URLhttp://localhost:3000Console base URL for bun run screenshots (the docs screenshot generator). Dev tooling only — not read by any service.

For the security-relevant subset of these variables organized as a go-live checklist, see the hardening guide.