Skip to main content

Environment Variable Reference

The complete, exhaustive list of every environment variable AIFactory reads — required vars, config, feature flags, tuning knobs, and credentials. If a fresh install "works here but breaks there", the cause is almost always a variable set in one environment and missing in the other. This page is the single source of truth for finding them.

For a shorter, opinionated tour of the feature flags that matter most, see the Configuration Reference. This page is the full enumeration; that page is the highlights.

How variables are read

AIFactory reads configuration from four distinct surfaces:

SurfaceNamingWhere it livesRead by
Pydantic SettingsAPP_-prefixedapps/web-server/.env or pod/Helm envapps/web-server/server/config.py
Direct os.environunprefixed (e.g. AIFACTORY_*, provider keys)process env, apps/backend/.env, apps/web-server/.env (loaded into os.environ at startup by server/env_bootstrap.py), or Helm envmodules across apps/backend/ and apps/web-server/server/
Per-project .aifactory/.envAIFACTORY_* / DELEGATE_*the target repo being builtPR-endgame + deploy flags; override the host per project
Frontend build envVITE_-prefixedapps/frontend-web build (Vite)baked into the static bundle at build time

A field named FOO on the pydantic Settings class is set with APP_FOO. Some variables (e.g. DATABASE_URL, DEBUG) are read both as an APP_ pydantic field and directly from os.environ elsewhere — set the unprefixed form and, for the pydantic field, the APP_-prefixed form; both are noted below.

Booleans are truthy on 1 / true / yes / on (case-insensitive) unless noted. Empty / unset means off / default.

:::note Authoritative source Defaults and behaviour are verified against the code at time of writing. The code is always the final authority — file paths are given for every variable so you can confirm. :::

Control plane (web server)

Set via APP_-prefixed env (pydantic Settings). All read in apps/web-server/server/config.py unless noted.

VariableDefaultRequiredPurpose
APP_HOST0.0.0.0noBind address for the web server.
APP_PORT3101noListen port.
APP_DEBUGfalseno (on=debug)Debug mode; also relaxes the APP_DISABLE_AUTH / wildcard-CORS boot guards to warn instead of refuse.
APP_API_TOKENauto-generatednoAdmin bearer token. Auto-generated to ~/.aifactory/.token (0600) on first run if unset. Also read directly as APP_API_TOKEN by intake/handoff clients.
APP_DISABLE_AUTHfalseno (on=bypass)Bypass auth — every request treated as admin. Local dev only; the server refuses to bind a non-loopback host with this on unless APP_DEBUG.
APP_JWT_SECRETauto-generatednoHMAC secret for JWTs. Persisted to ~/.aifactory/.jwt_secret if unset so tokens survive restarts.
APP_JWT_ACCESS_TOKEN_EXPIRE_MINUTES15noAccess-token lifetime.
APP_JWT_REFRESH_TOKEN_EXPIRE_DAYS7noRefresh-token lifetime.
APP_JWT_ALGORITHMHS256noJWT signing algorithm.
APP_SSL_ENABLEDfalsenoServe HTTPS; generates a self-signed cert if no paths given.
APP_SSL_CERTFILE""noPath to SSL certificate (with APP_SSL_ENABLED).
APP_SSL_KEYFILE""noPath to SSL private key (with APP_SSL_ENABLED).
DATABASE_URL / APP_DATABASE_URLsqlite+aiosqlite:///<data>/data.dbnoPrimary database URL. Set Postgres here for multi-replica. Read directly in server/database/engine.py, database/alembic/env.py, services/tenant_teardown.py, services/job_state_store.py, crypto/__main__.py.
APP_MIGRATIONS_AUTO_APPLYtruenoOn boot run alembic upgrade head. Set false in K8s where a Helm Job migrates out-of-band.
APP_PROJECTS_DATA_DIR / APP_PROJECTS_DATA_DIRdata dirnoDirectory for project metadata + SQLite. Also read directly by routes/copilot_mcp.py.
APP_BACKEND_PATH../backendnoPath to apps/backend. Also the build-Job backend path (build_backend.py).
APP_CORS_ORIGINSlocalhost:3000/3100/3101noAllowed CORS origins (comma-list or JSON array). A wildcard * disables credentialed CORS (refuses boot unless APP_DEBUG).
APP_DEFAULT_SHELL/bin/bashnoShell for the built-in terminal.
APP_MAX_TERMINALS20noMax concurrent terminal sessions.
APP_MAX_CONCURRENT_TASKS5noGlobal cap on concurrently running builds (RFC-0016 admission control); <= 0 = unlimited.
APP_AGENT_MONITOR_SYNC_INTERVAL3.0noSeconds between agent-monitor poll/sync ticks.
APP_AGENT_MAX_CONTINUATION_ROUNDS10noMax auto-continue rounds for a finished-but-incomplete build.
APP_REDIS_URL""noRedis for cross-replica WebSocket fan-out. Empty = in-process only (single replica).
APP_REDIS_CHANNELaifactory:eventsnoRedis pub/sub channel (override for shared Redis).
APP_WORKSPACE_S3_URI_BASE""nofsspec URI base (s3://, gs://, azure://) enabling workspace snapshot/restore. Empty = local PVC only.
APP_CFACTORY_SEARCH_URLhttp://cfactory.factory.svc.cluster.local:3111noCockpit base for federated command-palette (Cmd-K) search proxy (#149).
APP_CFACTORY_READ_KEY""noRead-scoped cockpit key for federated search. Empty = feature off.
APP_SKILLS_PATH(built-in)noOverride path to the agent skills directory. Read in services/skills_service.py.
APP_FILE_BROWSE_ROOTS""noExtra os.pathsep-separated roots the file browser may serve. Read in routes/files.py.
APP_PROJECTS_DATA_DIR(data dir)noCopilot-MCP project data root fallback (routes/copilot_mcp.py).

Authentication & provider credentials

AIFactory is OAuth-only by default for Claude: any ANTHROPIC_API_KEY in the environment is scrubbed from agents to prevent silent API billing. Read in apps/backend/core/auth.py, core/client.py, services/claude_token_pool.py.

VariableDefaultRequiredPurpose
CLAUDE_CODE_OAUTH_TOKEN(none)yes (Claude)Claude Code OAuth token — the primary credential. Set via claude setup-token, the portal OAuth flow, or explicitly. Without it a build dies No OAuth token found.
CLAUDE_CODE_OAUTH_TOKEN_POOL(none)noNewline/comma list of OAuth tokens for rotation/failover across replicas (claude_token_pool.py).
ANTHROPIC_AUTH_TOKEN(none)conditionalCCR/proxy bearer token for enterprise gateway setups.
ANTHROPIC_BASE_URLAnthropic APInoOverride the Anthropic endpoint (local proxy / gateway / self-hosted). Read in cli/utils.py, core/auth.py.
AIFACTORY_ALLOW_API_KEYoffnoOpt into direct ANTHROPIC_API_KEY auth (accepts the direct-billing path). Default OAuth-only.
AIFACTORY_HEADLESS_PREFER_API_KEYoffnoIn headless mode prefer an API key over OAuth when both present.
ANTHROPIC_API_KEY(none)conditionalDirect Anthropic key. Only honoured with AIFACTORY_ALLOW_API_KEY; also used by the Batch API helper (core/batch.py) and Graphiti. Otherwise scrubbed from agents.
ANTHROPIC_MODEL(SDK default)noModel-name override passed through to the agent SDK.
ANTHROPIC_DEFAULT_HAIKU_MODEL(SDK default)noOverride the concrete model the haiku alias resolves to (phase_config.py).
ANTHROPIC_DEFAULT_SONNET_MODEL(SDK default)noOverride the sonnet alias model ID.
ANTHROPIC_DEFAULT_OPUS_MODEL(SDK default)noOverride the opus / opus-1m alias model ID.
NO_PROXY(none)noStandard no-proxy list; SDK passthrough (usually with ANTHROPIC_BASE_URL).
DISABLE_TELEMETRY(none)noDisable Claude SDK telemetry; SDK passthrough.
DISABLE_COST_WARNINGS(none)noSuppress SDK cost warnings; SDK passthrough.
API_TIMEOUT_MS(SDK default)noAgent SDK request timeout (ms); SDK passthrough.

Non-Claude provider credentials

Resolved by phase routing / byo_llm and forwarded to build Jobs only when present. Read mainly in apps/backend/phase_config.py, providers/*, integrations/graphiti/config.py.

VariableDefaultRequiredPurpose
OPENAI_API_KEY(none)conditionalOpenAI key (QA/planner routing, Graphiti, changelog fallback).
OPENAI_MODELgpt-5-mini (Graphiti)noOpenAI model name (Graphiti LLM / OpenAI-compatible).
OPENAI_EMBEDDING_MODELtext-embedding-3-smallnoOpenAI embedding model (Graphiti).
OPENAI_COMPATIBLE_API_KEY(none)conditionalKey for a generic OpenAI-compatible endpoint.
OPENAI_COMPATIBLE_BASE_URL(none)conditionalBase URL for the OpenAI-compatible endpoint.
OPENAI_COMPATIBLE_MAX_TOKENS(provider default)noMax-tokens override for the OpenAI-compatible agent (providers/openai_compatible_agentic.py).
OPENAI_COMPATIBLE_REASONING_EFFORT(provider default)noReasoning-effort override for the OpenAI-compatible agent.
AZURE_OPENAI_API_KEY(none)conditionalAzure OpenAI key (Graphiti).
AZURE_OPENAI_BASE_URL(none)conditionalAzure OpenAI endpoint.
AZURE_OPENAI_LLM_DEPLOYMENT""noAzure OpenAI LLM deployment name.
AZURE_OPENAI_EMBEDDING_DEPLOYMENT""noAzure OpenAI embedding deployment name.
GEMINI_API_KEY(none)conditionalGoogle Gemini key (phase routing, CLI accounts).
GEMINI_CLI_TRUST_WORKSPACE(none)noGemini CLI workspace-trust passthrough for build Jobs.
GOOGLE_API_KEY(none)conditionalGoogle AI key (Graphiti, phase routing).
GOOGLE_LLM_MODELgemini-2.0-flashnoGoogle LLM model (Graphiti).
GOOGLE_EMBEDDING_MODELtext-embedding-004noGoogle embedding model (Graphiti).
OPENROUTER_API_KEY(none)conditionalOpenRouter aggregator key (Graphiti).
OPENROUTER_BASE_URLhttps://openrouter.ai/api/v1noOpenRouter base URL.
OPENROUTER_LLM_MODELanthropic/claude-3.5-sonnetnoOpenRouter LLM model.
OPENROUTER_EMBEDDING_MODELopenai/text-embedding-3-smallnoOpenRouter embedding model.
VOYAGE_API_KEY(none)conditionalVoyage AI embeddings key (Graphiti).
VOYAGE_EMBEDDING_MODELvoyage-3noVoyage embedding model.
OLLAMA_BASE_URLhttp://localhost:11434noOllama server URL (local/offline models).
OLLAMA_API_URL(fallback of OLLAMA_BASE_URL)noAlternate Ollama URL var (pfactory/tiers.py).
OLLAMA_API_KEY(none)noOllama Cloud key; build-Job passthrough.
OLLAMA_CLOUD_BASE_URL(none)noOllama Cloud base URL; build-Job passthrough.
OLLAMA_LLM_MODEL""conditionalOllama LLM model (required for Ollama LLM).
OLLAMA_EMBEDDING_MODEL""conditionalOllama embedding model (required for Ollama embeddings).
OLLAMA_EMBEDDING_DIM0conditionalOllama embedding dimension; must match the model.
OPENCODE_DEFAULT_MODEL""conditionalModel for the OpenCode agentic provider (providers/opencode_agentic.py).
GH_TOKEN / GITHUB_TOKEN(none)conditionalGitHub token for PR endgame, workspace fetch, testing (providers/factory.py, runners/github/runner.py). Required to open/merge PRs.
GITHUB_BOT_TOKEN(none)noSeparate bot identity for GitHub actions (runners/github/runner.py).
GITHUB_REPO(none)conditionalTarget repo for the GitHub runner CLI.

Recognized / scrubbed credentials

These are recognized as secrets and blanked from the agent environment (core/auth.py::_AGENT_ENV_DENY_EXACT) so a prompt-injected build can't exfiltrate them. Set them on the control plane only if a component genuinely needs them (cloud/KMS SDKs); they never reach agents.

VariablePurpose
GROQ_API_KEY, TOGETHER_API_KEY, MISTRAL_API_KEY, DEEPSEEK_API_KEY, LINEAR_API_KEYAdditional provider/integration keys; recognized and scrubbed.
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKENAWS credentials (boto3 / workspace store / deploy); scrubbed from agents.
GOOGLE_APPLICATION_CREDENTIALSGCP service-account path; scrubbed from agents.
AZURE_CLIENT_SECRETAzure app secret; scrubbed from agents.

Any host var whose name matches SECRET|PASSWORD|PRIVATE_KEY|CREDENTIAL|_KMS|PASSPHRASE is also blanked generically.

LiteLLM gateway & audit

Read in providers/_gateway.py, phase_config.py, core/enforcement.py, services/litellm_admin_client.py, services/llm_audit_hook.py, providers/openai_compatible.py.

VariableDefaultRequiredPurpose
LITELLM_GATEWAY_URL""conditionalRoute model calls through a LiteLLM gateway. Empty = direct providers.
LITELLM_API_KEY""conditionalAuth key for the LiteLLM gateway.
LITELLM_MASTER_KEY""conditionalMaster key for LiteLLM admin operations.
LITELLM_MASTER_KEY_WRAPPED""noKMS-wrapped master key (base64) unwrapped at runtime.
LITELLM_AUDIT_SCRUB_OUTBOUNDoffnoScrub secrets from outbound LiteLLM audit payloads.
LITELLM_AUDIT_EXTRA_PATTERNS""noExtra comma-separated regex patterns for the audit scrubber.

Model, agent & routing tuning

Read across apps/backend/ (routing_policy.py, core/model_config.py, agents/*, prompts_pkg/prompts.py, spec/pipeline/agent_runner.py, solo_mode.py, integrations/bmad/session_config.py, core/enforcement.py).

VariableDefaultRequiredPurpose
AIFACTORY_ROUTING_POLICY""noNamed per-phase model routing policy (routing_policy.py).
AUTO_BUILD_MODEL(built-in)noDefault model for CLI auto-builds (cli/main.py).
UTILITY_MODEL_ID(built-in)noModel for small utility completions (core/model_config.py).
UTILITY_THINKING_BUDGET""noThinking-token budget for the utility model.
QA_LLM_PROVIDER""noForce the QA phase to a specific provider (phase_config.py).
INSIGHT_EXTRACTION_ENABLEDtrueno (off disables)Enable session-insight extraction (analysis/insight_extractor.py).
INSIGHT_EXTRACTOR_MODEL(built-in)noModel used for insight extraction.
AIFACTORY_SOLO_MODEoffnoSingle self-directed agent for small jobs (token-saving).
QUICK_MODEoffnoShorter prompts / quick path (prompts_pkg/prompts.py, spec/pipeline/agent_runner.py).
BMAD_SESSION_SEGMENTATIONoffnoPer-story session segmentation for large tasks.
USE_CLAUDE_MDoffnoInject the repo CLAUDE.md into the agent context (core/client.py).
DELEGATE_BY_DEFAULToffnoDelegate the coder phase by default (per-project .aifactory/.env).
AIFACTORY_AGENT_STALL_TIMEOUT600noSeconds before an agent with no output is considered stalled.
AIFACTORY_FIRST_TOKEN_TIMEOUT120noSeconds to wait for the agent's first token (agents/session.py).
AIFACTORY_MAX_RATE_LIMIT_WAIT_SECONDS3300noCap on backoff waiting out a provider rate limit (agents/base.py).
AIFACTORY_GUARDRAIL_REPEAT_FAIL5noRepeated-failure count that trips the guardrail (agents/guardrails.py).
AIFACTORY_GUARDRAIL_TOOL_FAIL_HALT8noTool-failure count that halts the agent.
AIFACTORY_GUARDRAIL_NOPROGRESS5noNo-progress count that trips the guardrail.
AIFACTORY_ACT_GUARDRAILoffnoEnable the act-loop guardrail hook (agents/act_loop_hooks.py).
AIFACTORY_CONTEXT_SUMMARYoffnoEnable rolling context summarization (agents/context_summary.py).
AIFACTORY_MUTATION_LEDGERoffnoRecord agent file mutations to a ledger (agents/mutation_ledger.py).
AIFACTORY_TEST_EVIDENCE_GATEoffnoHonesty gate: no green test checkbox unless a real test command ran (agents/test_evidence.py).
AIFACTORY_SELF_HEALoffnoEnable the pre-merge security self-heal gate (agents/self_heal_integration.py, core/worktree.py).
AIFACTORY_CLAUDE_ENFORCEMENT_ENABLEDoffnoEnable per-org Claude enforcement (core/enforcement.py).
AIFACTORY_CLAUDE_ENFORCEMENT_FAILURE_MODEopennoopen (fail-open) or closed (fail-closed) when enforcement can't decide.
AIFACTORY_GRAPHIFY_ENABLEDoffnoOpt into the Graphify code-graph MCP tool for the coder (core/client.py); forwarded to build Jobs.
AIFACTORY_TEST_AGENT_CMD""noOverride the agent command (test hook, services/agent_service.py).
AIFACTORY_GIT_LOCK_TIMEOUT120noSeconds to wait for the per-repo git lock (core/worktree.py).
DEFAULT_BRANCHauto-detectnoBase branch for worktree creation (core/worktree.py, cli/workspace_commands.py).
PROJECT_WORKSPACE_ROOT(data dir)noRoot for project workspaces (services/project_workspace_service.py).
CIruntime-detectednotrue selects non-interactive / CI reviewer behaviour (cli/spec_commands.py, review/reviewer.py). Also set in the fixed build-Job env.
ENABLE_FANCY_UItruenoRich terminal UI vs plain text (ui/capabilities.py).
FACTORY_SERVICE_NAMEthe FactorynoService name string in GitLab-provider messages.
EMAIL_OAUTH_REDIRECT_URI(derived)noOverride the email-integration OAuth redirect (routes/email.py).

Build backend & job-native scheduling

How run.py executes and what lets a build schedule on any node. Read in apps/web-server/server/services/build_backend.py, core/nix_env.py, core/workspace_fetch.py, agents/gate_runner.py.

VariableDefaultRequiredPurpose
AIFACTORY_BUILD_BACKENDsubprocessnosubprocess (in-pod asyncio) or kubejob (each build as its own k8s Job). Unknown values fall back to subprocess.
AIFACTORY_IMAGE(built-in)noRunning image; fallback build-Job image when AIFACTORY_BUILD_IMAGE unset.
AIFACTORY_BUILD_IMAGE(unset)noOverride the image for the kubejob build Job only. Point at a :sha-<short>-nix tag for in-image Nix.
AIFACTORY_PACK_WORKSPACEoffnoPack /work to object storage and unpack in the Job (removes the workspace node-pin).
AIFACTORY_PACKED_NIX_IN_IMAGEoffnoResolve /nix from the build image instead of the Nix-store PVC (removes the last node-pin).
WORKSPACE_URI""conditionalObject-storage URI the Job unpacks /work from (set by the pack path; core/workspace_fetch.py).
AIFACTORY_DATA_ROOT/home/nonroot/.aifactorynoData PVC mount root; used to compute worktree subPaths and sandbox mounts.
AIFACTORY_SANDBOX_NAMESPACEfactorynoK8s namespace the build Job runs in.
AIFACTORY_BUILD_SAaifactory-sandboxnoServiceAccount for the build Job.
AIFACTORY_BUILD_DEADLINE_SECONDS21600 (6h)noActive-deadline for the build Job.
AIFACTORY_SANDBOX_REPO_PVCaifactory-datanoPVC holding the repo/worktree (co-mounted into the Job).
AIFACTORY_NIX_STORE_PVC""noWarm Nix-store PVC to mount into the Job. Empty on the in-image-Nix path.
AIFACTORY_CLI_CREDS_SECRET""noK8s Secret name holding CLI credentials mounted into the build Job.
APP_BACKEND_PATH/home/projects/MagesticAI/apps/backendnoBackend path inside the build Job.

Build-Job env allowlist (_PASSTHROUGH_BUILD_ENV)

A kubejob build runs in a fresh pod. build_job_env() seeds it with a fixed non-interactive env plus a passthrough allowlist — control-plane vars are forwarded only when present, in env (never argv). Anything not on the allowlist (notably ANTHROPIC_API_KEY and control-plane secrets) never reaches the Job.

Fixed env always set: PYTHONUNBUFFERED=1, PYTHONIOENCODING=utf-8, CLAUDE_CODE_ENTRYPOINT=cli, CI=true, plus CLAUDE_CODE_OAUTH_TOKEN (the pooled token).

Forwarded when present: ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN, ANTHROPIC_MODEL, ANTHROPIC_DEFAULT_HAIKU_MODEL, ANTHROPIC_DEFAULT_SONNET_MODEL, ANTHROPIC_DEFAULT_OPUS_MODEL, NO_PROXY, DISABLE_TELEMETRY, DISABLE_COST_WARNINGS, API_TIMEOUT_MS, GITHUB_TOKEN, GH_TOKEN, OPENAI_API_KEY, OPENAI_COMPATIBLE_API_KEY, OPENAI_COMPATIBLE_BASE_URL, GEMINI_API_KEY, GEMINI_CLI_TRUST_WORKSPACE, GOOGLE_API_KEY, OLLAMA_API_KEY, OLLAMA_CLOUD_BASE_URL, S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY, S3_SECRET_KEY, S3_REGION, AIFACTORY_GRAPHIFY_ENABLED.

The S3_* namespace is what the Job-side core/artifact_store reads to reconstitute a packed workspace (distinct from the chart's fsspec AIFACTORY_S3_* / AWS_* WorkspaceStore vars below). All forwarded only when non-empty.

Execution mode & parallelism

VariableDefaultRequiredPurpose
AIFACTORY_INTAKE_PARALLELoffnoFleet default: unlabelled issue-intake builds run parallel waves. Per-issue labels win.
AIFACTORY_INTAKE_WORKERS(unset)noWorker cap for parallel intake builds; unset uses the coder default (3).

run.py --parallel / --workers N are CLI flags, not env vars. Per-issue labels (factory:parallel, factory:serial, factory:workers=N) are covered in the Configuration Reference.

Intake poller

Read in apps/web-server/server/services/intake_poller.py, apps/backend/intake/*, routes/from_issue.py.

VariableDefaultRequiredPurpose
AIFACTORY_INTAKE_POLLERoffnoEnable the background issue-intake poller.
AIFACTORY_INTAKE_REPOS""conditionalComma-separated repos the poller watches.
AIFACTORY_INTAKE_INTERVAL_S30noPoll interval (min 5s).
AIFACTORY_INTAKE_REQUEUE_AFTER_S600noRequeue a stuck in-flight issue after N seconds (min 60).
AIFACTORY_INTAKE_DB(data dir)noOverride the processed-issue store path (intake/processed_store.py).
AIFACTORY_INTAKE_AUTO_HANDOFFoffnoAuto-hand off to the next stage after an intake build (routes/from_issue.py).
AIFACTORY_TOKEN(none)conditionalAIFactory API token for the poller's own calls (falls back to APP_API_TOKEN).
AIFACTORY_URL""conditionalAIFactory base URL for poller callbacks.
PFACTORY_TOKEN(none)noPFactory token for ingest (falls back to APP_API_TOKEN).
PFACTORY_INGEST_URL""noPFactory ingest endpoint the poller posts to.

PR endgame, deploy & Copilot

Read in apps/web-server/server/services/pr_endgame.py, services/copilot_dispatch_service.py, routes/github.py. These are commonly set per-project in .aifactory/.env.

VariableDefaultRequiredPurpose
AIFACTORY_AUTO_PRoffnoAuto-open a PR on a clean build.
AIFACTORY_AUTO_MERGEoffnoAuto-merge after the reviewer approves (requires AIFACTORY_AUTO_PR).
AIFACTORY_PR_REVIEWERaifactorynoWhich review gates merge: aifactory, copilot, or any.
AIFACTORY_AUTO_DEPLOYoffnoDeploy-then-verify to AWS App Runner on a clean build, then tear down.
AIFACTORY_COPILOT_DISPATCH_ENABLEDoffnoEnable GitHub Copilot dispatch for reviews.

Completion events & outbox

RFC-0001 PARR spine. Read in apps/web-server/server/services/completion.py, services/outbox.py. Best-effort — a missing target never breaks a build.

VariableDefaultRequiredPurpose
AIFACTORY_COMPLETION_WEBHOOK(none)noURL to POST normalized completion events (cockpit ingress :3111/api/events).
AIFACTORY_COMPLETION_WEBHOOK_TIMEOUT5noTimeout (seconds) for the completion webhook.
AIFACTORY_COMPLETION_SENTINELoffnoAlso write <spec_dir>/COMPLETED.json on completion.
AIFACTORY_COMPLETION_OUTBOXoffnoEnable the durable completion outbox (retry queue).
AIFACTORY_COMPLETION_OUTBOX_DB(data dir)noOverride the outbox DB path.
AIFACTORY_EVENT_SOURCE/aifactorynoCloudEvents source for emitted events.
AIFACTORY_WORKER_PROGRESS_INTERVAL_S(built-in)noInterval for worker progress heartbeat events.

MCP control plane

Read in apps/web-server/server/mcp_remote/*, routes/copilot_mcp.py, apps/backend/mcp_server/*, agents/tools_pkg/http_client.py, core/mcp_credentials.py, agents/tools_pkg/mcp_catalog.py.

VariableDefaultRequiredPurpose
AIFACTORY_MCP_REMOTE_ENABLEDoffnoExpose the HTTP+SSE MCP server at /api/mcp-remote/sse.
AIFACTORY_MCP_LOOPBACK_URLhttp://localhost:3101noLoopback URL MCP tools call back into.
AIFACTORY_MCP_SECRET""conditionalShared secret for the Copilot MCP endpoint.
AIFACTORY_MCP_KEY""noScoped MCP key (acw_) the agent HTTP client presents.
AIFACTORY_API_URLhttp://localhost:3101noBase URL the agent tools call the web server on.
AIFACTORY_API_TOKEN_FILE~/.aifactory/.tokennoFile the agent tools read the API token from.
AIFACTORY_SPEC_DIR(none)noSpec directory the MCP server serves (mcp_server/aifactory_server.py).
AIFACTORY_PROJECT_DIR(cwd)noProject directory for the MCP server (falls back to CLAUDE_PROJECT_DIR).
GCP_MCP_ENDPOINT(built-in default)noEndpoint for the GCP MCP catalog tool.
GRAPHITI_MCP_URL(none)conditionalEnables the Graphiti MCP memory tool when set (agents/tools_pkg/models.py, core/client.py).

Live Console (rmux)

Read in apps/web-server/server/rmux/*.

VariableDefaultRequiredPurpose
AIFACTORY_RMUX_ENABLED / APP_RMUX_ENABLEDoffnoEnable the streaming rmux Live Agent Console.
AIFACTORY_RMUX_PANES_DIR(data dir)noWritable directory for rmux FIFO panes.

Security & sandbox

Read in apps/web-server/server/services/sandbox.py, apps/backend/agents/gate_runner.py, agents/coder.py, core/client.py, runners/ai_analyzer/claude_client.py, project/models.py, security/egress.py.

VariableDefaultRequiredPurpose
AIFACTORY_BASH_SANDBOXonnoGate the bubblewrap syscall sandbox; set off where bwrap can't mount /proc.
AIFACTORY_AGENT_SANDBOXoffnoOS-level agent sandbox mode (services/sandbox.py).
AIFACTORY_AGENT_SANDBOX_PIDNSoffnoOpt into a private PID namespace for the sandbox.
AIFACTORY_SANDBOX_BACKENDdockernoGate-runner sandbox backend (docker / nixjob).
AIFACTORY_SANDBOX_GATESoffnoRun agent gates inside the sandbox.
AIFACTORY_SANDBOX_IMAGE""noImage for sandboxed gate runs.
AIFACTORY_SANDBOX_NETWORKnonenoNetwork mode for the sandbox.
AIFACTORY_SANDBOX_REPO_PVCaifactory-datanoRepo PVC for sandboxed gate runs.
AIFACTORY_EXTRA_ALLOWED_COMMANDS""noExtend the dynamic command allowlist (project/models.py).
AIFACTORY_EGRESS_POLICYoffnoAgent egress policy mode (security/egress.py).
AIFACTORY_EGRESS_ALLOWED_HOSTS""noAllowed egress hosts when the egress policy is on.
METRICS_SCRAPE_TOKEN""noBearer token gating the /metrics scrape endpoint (observability/metrics.py).

Identity providers (OIDC / SAML / SCIM / email OAuth)

Read in apps/web-server/server/oidc/*, saml/*, scim/*, identity_providers.py, routes/oidc_routes.py, _get_email_oauth_credentials.py.

OIDC

VariableDefaultRequiredPurpose
APP_OIDC_ENABLEDoffnoEnable OIDC login.
APP_OIDC_ISSUER_URL(none)conditionalOIDC issuer URL (required when enabled).
APP_OIDC_CLIENT_ID(none)conditionalOIDC client ID.
APP_OIDC_CLIENT_SECRET(none)conditionalOIDC client secret.
APP_OIDC_PROVIDERkeycloaknoProvider preset (oidc/presets.py).
APP_OIDC_SCOPEpreset defaultnoOIDC scopes.
APP_OIDC_REDIRECT_URI(derived)noOverride the OIDC redirect URI.
APP_OIDC_POST_LOGIN_REDIRECT/noPost-login redirect path.
APP_OIDC_POST_LOGOUT_REDIRECT/noPost-logout redirect path.
APP_OIDC_DEFAULT_ROLEmembernoRole assigned to new OIDC users.
APP_OIDC_GROUP_TO_ROLE""noJSON map of IdP groups to roles.
APP_OIDC_DEFAULT_ORG_SLUGdefaultnoDefault org slug for provisioned users.
APP_OIDC_DEFAULT_ORG_NAMEDefault OrganizationnoDefault org display name.
APP_OIDC_USERINFO_CACHE_TTL_S(built-in)noTTL for cached OIDC userinfo.
OIDC_DISPLAY_NAMEOIDC (default)noLogin-button label (identity_providers.py).

SAML

VariableDefaultRequiredPurpose
SAML_ENABLEDoffnoEnable SAML SSO.
SAML_IDP_NAMEsamlnoInternal IdP identifier.
SAML_IDP_DISPLAY_NAME(= name)noLogin-button label.
SAML_SP_ENTITY_ID""conditionalService-provider entity ID.
SAML_ACS_URL""conditionalAssertion Consumer Service URL.
SAML_IDP_METADATA_URL(none)conditionalIdP metadata URL (or use the file form).
SAML_IDP_METADATA_FILE(none)conditionalPath to IdP metadata XML.
SAML_SP_CERT_FILE(none)noSP certificate file path.
SAML_SP_KEY_FILE(none)noSP private-key file path.
SAML_SP_CERT_PREVIOUS_FILE(none)noPrevious SP cert (rotation).
SAML_REQUIRE_ENCRYPTED_ASSERTIONoffnoRequire encrypted assertions.
SAML_IDP_INIT_DEFAULT_RETURN_TO""noDefault return-to for IdP-initiated login.
SAML_SLO_ENABLEDoffnoEnable Single Logout.
SAML_SLO_URL(none)noSP SLO endpoint.
SAML_IDP_SLO_URL(none)noIdP SLO endpoint.

SCIM & email OAuth

VariableDefaultRequiredPurpose
SCIM_ENABLEDoffnoEnable the SCIM provisioning endpoint (main.py).
SCIM_BEARER_TOKEN""conditionalBearer token gating SCIM (scim/auth.py).
APP_EMAIL_GOOGLE_CLIENT_ID(none)conditionalGoogle email-integration OAuth client ID.
APP_EMAIL_GOOGLE_CLIENT_SECRET(none)conditionalGoogle email-integration OAuth secret.
APP_EMAIL_MICROSOFT_CLIENT_ID(none)conditionalMicrosoft email-integration OAuth client ID.
APP_EMAIL_MICROSOFT_CLIENT_SECRET(none)conditionalMicrosoft email-integration OAuth secret.

Multi-tenant, crypto (KMS) & audit anchor

Read in apps/web-server/server/tenancy.py, services/tenant_reconciler.py, services/tenant_teardown.py, crypto/kms/*, services/audit_anchor.py, jobs/audit_anchor_cron.py, services/vault_client.py, crypto/kms/vault.py.

VariableDefaultRequiredPurpose
AIFACTORY_MULTI_TENANToffnoEnable multi-tenant isolation (tenancy.py).
TENANT_ISOLATION_ENABLEDoffnoGate tenant-teardown isolation logic.
TENANT_NAMESPACE_PREFIXaifactory-tenantnoNamespace prefix for per-tenant namespaces.
TENANT_DELETION_GRACE_DAYS30noGrace period before tenant deletion.
TENANT_TEARDOWN_DRY_RUN_HOURS24noDry-run window before teardown executes.
TENANT_CLOUD_BACKENDawsnoCloud backend for tenant reconciliation.
TENANT_CNI_BACKENDautonoCNI backend for tenant network policy.
TENANT_S3_BUCKET""noS3 bucket for per-tenant prefixes (empty = skip).
TENANT_OIDC_PROVIDER_ARN""noIRSA OIDC provider ARN (empty = skip IAM role).
TENANT_ALLOWED_FQDNSapi.anthropic.comnoAllowed-FQDN egress list for tenants.
TENANT_QUOTA_PODS50noPer-tenant pod quota.
TENANT_QUOTA_PVCS10noPer-tenant PVC quota.
TENANT_LIMIT_CPU500mnoDefault per-tenant CPU limit.
TENANT_LIMIT_MEMORY512MinoDefault per-tenant memory limit.
KMS_BACKEND / APP_KMS_BACKENDfernetnoEnvelope-encryption backend: fernet, aws, gcp, azure, vault.
KMS_FERNET_KEY / APP_KMS_FERNET_KEY(none)conditionalFernet key for the fernet KMS backend.
AWS_KMS_KEY_ID(none)conditionalKMS key ID (AWS backend).
AWS_REGION(none)noAWS region for KMS/services.
AWS_ENDPOINT_URL(none)noCustom AWS endpoint (e.g. LocalStack).
GCP_KMS_KEY_NAME(none)conditionalKMS key name (GCP backend).
AZURE_KEYVAULT_URL(none)conditionalKey Vault URL (Azure backend).
AZURE_KEYVAULT_KEY(none)conditionalKey Vault key name (Azure backend).
VAULT_ADDR(none)conditionalHashiCorp Vault address (Vault backend / vault client).
VAULT_TOKEN(none)conditionalVault token.
VAULT_NAMESPACE(none)noVault namespace (Enterprise).
VAULT_TRANSIT_KEY(default)noVault transit key name.
VAULT_TRANSIT_MOUNT(default)noVault transit mount point.
AUDIT_ANCHOR_PER_TENANTfalsenoAnchor the audit chain per tenant.
AUDIT_ANCHOR_BATCH_SIZE100noRows per anchor batch.
AUDIT_ANCHOR_KEY_CACHE_SIZE1000noPer-tenant anchor-key cache size.
AUDIT_ANCHOR_GENESISGENESISnoGenesis sentinel for an empty audit chain.

Graphiti memory

Enable/disable and provider selection for the persistent memory layer. Read in apps/backend/integrations/graphiti/config.py, query_memory.py. Provider credential/model vars (OPENAI_*, AZURE_OPENAI_*, VOYAGE_*, GOOGLE_*, OPENROUTER_*, OLLAMA_*, ANTHROPIC_API_KEY, GRAPHITI_ANTHROPIC_MODEL) are listed under provider credentials.

VariableDefaultRequiredPurpose
GRAPHITI_ENABLEDtrueno (off disables)Enable the Graphiti memory layer.
GRAPHITI_LLM_PROVIDERopenainoLLM provider: openai/anthropic/azure_openai/ollama/google/openrouter.
GRAPHITI_EMBEDDER_PROVIDERopenainoEmbedder provider: openai/voyage/azure_openai/ollama/google/openrouter.
GRAPHITI_DATABASEmagestic_ai_memorynoLadybugDB database name.
GRAPHITI_DB_PATH~/.magestic-ai/memoriesnoLadybugDB storage path.
GRAPHITI_ANTHROPIC_MODELclaude-sonnet-4-5noAnthropic model when Graphiti LLM = anthropic.

Observability & tracing

Read in apps/web-server/server/observability/*, apps/backend/core/tracing_bootstrap.py.

VariableDefaultRequiredPurpose
OTEL_EXPORTER_OTLP_ENDPOINT""noOTLP endpoint; set enables OpenTelemetry export.
OTEL_EXPORTER_OTLP_PROTOCOLgrpcnoOTLP protocol (grpc/http).
OTEL_SERVICE_NAMEaifactory-webnoService name for web-server traces/metrics.
OTEL_SERVICE_NAME_AGENTaifactory-agentnoService name for agent traces.

TFactory / PFactory handoff

Read in apps/backend/pfactory/tfactory_client.py, pfactory/tiers.py, services/agent_service.py. (Poller-side PFACTORY_* / AIFACTORY_URL vars are under Intake poller.)

VariableDefaultRequiredPurpose
TFACTORY_BASE_URL""conditionalTFactory base URL; unset disables the handoff (a local marker is still written).
TFACTORY_TOKEN(none)noBearer token for TFactory (falls back to APP_API_TOKEN).
TFACTORY_HANDOFF_PATH/api/handoffnoTFactory handoff endpoint path.
TFACTORY_PROJECT_ID(derived)noTFactory project ID for the handoff (tfactory_client.py).

Workspace object storage (fsspec WorkspaceStore)

Read in apps/web-server/server/services/workspace_store.py. Distinct from the Job-side S3_* namespace in the build allowlist.

VariableDefaultRequiredPurpose
AIFACTORY_S3_ENDPOINT_URL(none)noCustom S3 endpoint for the fsspec workspace store (MinIO etc.).
AIFACTORY_S3_ADDRESSING_STYLE(provider default)noS3 addressing style (path/virtual).

Batch API (Anthropic Message Batches)

Read in apps/backend/analysis/insight_extractor.py (helper: core/batch.py).

VariableDefaultRequiredPurpose
AIFACTORY_BATCH_MIN_JOBS2noBelow this many concurrent completions, skip the batch path.
AIFACTORY_BATCH_TIMEOUT120noHard timeout (seconds) for the batch poll loop.
AIFACTORY_BATCH_DISABLEoffnoKill-switch forcing the sequential path.

Frontend build (Vite)

Baked into the static bundle at build time. Read in apps/frontend-web/src/*.

VariableDefaultRequiredPurpose
VITE_API_URL(same-origin)noBackend base URL for integration settings.
VITE_API_BASE_URL/apinoAPI base path for the API client.
VITE_WS_BASE_URL(derived)noWebSocket base URL.
VITE_DEBUGoffnoEnable verbose frontend debug logging (with DEV).
VITE_PFACTORY_URLhttps://pfactory.freundcloud.org.uknoPlan-portal link in the portal switcher.
VITE_AIFACTORY_URLhttps://aifactory.freundcloud.org.uknoBuild-portal link in the portal switcher.
VITE_TFACTORY_URLhttps://tfactory.freundcloud.org.uknoTest-portal link in the portal switcher.
VITE_CFACTORY_URLhttps://cfactory.freundcloud.org.uknoCockpit link in the portal switcher.

Trusted-plan ingest

VariableDefaultRequiredPurpose
AIFACTORY_TRUSTED_PLAN_KEY_<AUTHORITY>(none)noHMAC key verifying a signed Task Contract v2 from an upstream authority (e.g. AIFACTORY_TRUSTED_PLAN_KEY_PFACTORY). See Task Contract. Read in apps/backend/trusted_plan.py.

Debug helpers

Read in apps/backend/core/debug.py, ui/status.py, and various runners.

VariableDefaultRequiredPurpose
DEBUG / APP_DEBUGoffnoEnable debug logging across backend + web server.
DEBUG_LEVEL1noDebug verbosity: 1=basic, 2=detailed, 3=verbose.
DEBUG_LOG_FILE(stdout)noWrite debug logs to a file instead of stdout.

Intentionally excluded (incidental)

These are read by the code but are pure OS / runtime / toolchain noise an operator does not configure for AIFactory. They are listed here so the reconciliation below accounts for every grepped name.

  • OS / shell / terminal: PATH, HOME, PWD, TERM, SHELL, EDITOR, VISUAL, NO_COLOR, FORCE_COLOR, XDG_RUNTIME_DIR, XDG_CACHE_HOME.
  • Python / Node runtime: PYTHONPATH, PYTHONUNBUFFERED, PYTHONIOENCODING, NODE_ENV, DEV (Vite build-mode flag).
  • Injected by the Claude Code CLI / k8s, not set by operators: CLAUDE_CODE_ENTRYPOINT, CLAUDE_PROJECT_DIR, KUBECONFIG, TRACEPARENT (W3C trace-context propagation).
  • Analyzer example strings / test fixtures (not AIFactory config — they are literals inside code that scans other repos, or pytest fixtures): VAR, VAR_NAME, API_KEY, QUOTED, ALREADY_SET, and the bare PORT example in analysis/analyzers/port_detector.py (the real port is APP_PORT).

Completeness

This reference was reconciled against an exhaustive grep of the codebase (os.environ, os.getenv, getenv(, env.get(, process.env, import.meta.env, every APP_-prefixed pydantic Settings field, the _PASSTHROUGH_BUILD_ENV allowlist, and every indirected _ENV_* constant).

  • 298 unique variable names found in code.
  • 283 documented above (including the _PASSTHROUGH_BUILD_ENV allowlist and recognized/scrubbed credentials).
  • 15 intentionally excluded as incidental (OS/runtime noise, CLI-injected vars, the analyzer PORT example) — a subset of the fuller excluded list above, which also names OS vars read only via env.get/fixtures.
  • 0 unaccounted for.