agent-swarm.devagent-swarm.dev
Reference

Environment Variables

Complete reference for all Agent Swarm environment variables — server configuration, API keys, MCP base URL, Slack tokens, GitHub credentials, and integration settings for self-hosted deployments

Complete reference for all environment variables used by Agent Swarm.

Server Variables

VariableDefaultDescription
PORT3013Port for MCP HTTP server
AGENT_SWARM_API_KEY—Preferred, namespaced API key for server authentication — takes precedence over API_KEY (set either one). Always read via getApiKey() (src/utils/api-key.ts), never process.env.API_KEY / process.env.AGENT_SWARM_API_KEY directly.
API_KEY—Legacy API key for server authentication (required unless AGENT_SWARM_API_KEY is set). See AGENT_SWARM_API_KEY above for the preferred, namespaced variable.
RBAC_ENABLEDtrueGates REST requests authenticated with aswt_ user tokens against RBAC role grants. Operator API-key requests and agent-authenticated calls are unaffected. Built-in roles and missing user assignments are seeded at boot. Set false to opt out.
EXTENSION_ALLOW_LEAD_ACTIVATIONtrueAllows trusted leads to enable, disable, and activate extension versions. Set false or 0 in the API deployment environment and restart every replica to reserve these operations for operators/dashboard users. Cannot be changed through swarm configuration APIs. Existing enabled extensions continue to run. See extension trust and activation.
EXTENSION_ALLOW_INLINE_INSTALLfalseReloadable boolean. Lets lead agents, the operator, and dashboard users install an extension from an inline manifest plus files instead of only from the catalog. Off keeps installs catalog-only (inline_install_disabled). Workers are denied either way. Installs land disabled. See inline install
SCRIPTS_ONLY_MCPfalse (unset/off)Experimental code-mode. Set to true on the API server and agent containers to expose only the eight script tools over MCP while keeping the full swarm SDK available through script-run. It can also be configured per agent/repository/global scope through swarm_config; see Scripts-only mode.
MCP_BASE_URLhttps://api.example-swarm.devInternal/worker-facing API base — the URL workers, the UI, and the setup command use to reach the API server. In split deploys this may be an internal/cluster address.
PUBLIC_MCP_BASE_URLFalls back to MCP_BASE_URLPublic, browser/externally-reachable API origin used for OAuth redirect URIs and webhook URLs. Defaults to MCP_BASE_URL when unset; set it in split deploys where MCP_BASE_URL is an internal/cluster address that external providers (Linear, Jira, GitHub) can't reach.
MCP_OAUTH_ALLOW_PRIVATE_HOSTSfalseWhen true, disables the SSRF guard that otherwise refuses localhost/RFC1918 private-IP hosts when the server connects to an operator-registered MCP server's OAuth discovery/token endpoints. Only needed to reach an internal-only MCP server, or for local dev/testing — keep the default deny-list in production.
ALLOW_PRIVATE_NETWORK_URLSfalseWhen true, allows localhost/private-IP hosts and http:// URLs for MCP server registration and proxying, onboarding LLM base URLs, OAuth app endpoints, and script connections. Development and test (NODE_ENV=development or test) allow them without it. Deployed images leave NODE_ENV unset, so they now deny private and loopback endpoints on these paths unless this is true. It does not affect MCP OAuth endpoints: those accept http:// only under NODE_ENV=development or test, and private hosts only with MCP_OAUTH_ALLOW_PRIVATE_HOSTS.
CORS_ALLOWED_ORIGINSHosted/dev allowlist (below)Comma-separated exact origins or wildcard-subdomain patterns. Unset or blank uses built-in defaults; a nonblank value replaces them. Unlisted origins receive neither Allow-Origin nor Allow-Credentials unless the non-credentialed compatibility option below is enabled. Applies to API and page-session endpoints.
CORS_ALLOW_ANY_ORIGINfalseDeployment-only compatibility opt-out (requires API restart; cannot be stored in Configuration): reflect any origin for non-credentialed requests only (credentials: "omit" for bearer clients). Cookie-authenticated responses still require an allowlisted origin; this flag never grants credentials to unlisted origins. Warns once per process when enabled. Prefer listing trusted SPA origins instead.
OAUTH_KEEPALIVE_DISABLEfalseDisables the background job that proactively refreshes Linear/Jira OAuth refresh tokens every 12h to prevent silent expiry.
SWARM_URLlocalhostBase domain for service discovery
APP_URL—Dashboard URL for Slack message links. DASHBOARD_URL is a deprecated alias of APP_URL.
SWARM_DASHBOARD_URLFalls back to APP_URLOptional override for the dashboard base URL used to build the "View in Agent Swarm" link posted back to a Linear AgentSession.
TRUST_BODY_REQUESTED_BY_USER_IDtrue (unset/on)When on (default), the server may honor a body-supplied requestedByUserId on POST /api/tasks (validated against a real user row) if no authenticated/owned-task identity is available — this keeps UI/API task attribution working under a shared operator key. Set to false in multi-tenant deployments where callers of the shared key are not all equally trusted, restoring the strict anti-spoofing behavior (body field ignored for operator/global-key callers).
STEERING_ENABLEDtrueTask steering is enabled by default. Set false or 0 (API server and worker containers) to opt out: steering routes reject writes, steer-task/accept-steer are not registered, and workers skip steering delivery polls.
ENV—Environment mode (development adds prefix to Slack agent names)
DATABASE_PATH./agent-swarm-db.sqliteSQLite database file path
SESSION_LOG_RETENTION_DAYS— (disabled)Permanently delete session_logs rows older than this many whole days. Accepted range: 1–1,000,000. Leave unset to disable this table's sweep. Start with DB_RETENTION_DRY_RUN=true; deleted session transcripts cannot be restored without a backup. See Database retention.
AGENT_LOG_RETENTION_DAYS— (disabled)Permanently delete agent_log rows older than this many whole days. Accepted range: 1–1,000,000. Leave unset to disable this table's sweep. Deleted task and agent history cannot be restored without a backup. See Database retention.
EVENTS_RETENTION_DAYS— (disabled)Permanently delete events rows older than this many whole days. Accepted range: 1–1,000,000. Leave unset to disable this table's sweep. Event aggregates become retention-window totals, and deleted telemetry cannot be restored without a backup. See Database retention.
CONTEXT_VERSIONS_KEEP_LATEST— (disabled)Keep only this many newest context_versions rows per agent and field, and permanently delete the older ones. Accepted range: 1–1,000,000. The newest version of every field is never deleted, and age plays no part. Leave unset to disable this table's sweep. Deleted profile history cannot be restored without a backup. See Database retention.
DB_RETENTION_DRY_RUNfalseWhen true or 1, report the exact number of rows each enabled retention policy would delete without deleting or vacuuming data. Only true, false, 1, and 0 are accepted through configuration writes. Use dry run before every first production activation.
DB_RETENTION_TICK_BUDGET_MS30000Wall-clock budget for one retention tick, shared across enabled tables. Accepted range: 1,000–300,000 ms. Read on every tick; out-of-range environment values fall back to the default.
DB_RETENTION_CATCHUP_INTERVAL_MS60000Delay before another retention tick when at least one table remains undrained. Accepted range: 5,000–3,600,000 ms. The normal cadence returns to hourly after the backlog drains.
DB_RETENTION_MAX_STATEMENT_MS250Target maximum driver execution time for one retention DELETE. Accepted range: 25–5,000 ms; the adaptive batch sizer tunes each table toward this ceiling.
ASSET_KEY_AUDIT_DISABLE_STARTUP_HARD_FAILfalseTemporary recovery switch. When true, the startup asset-namespace audit logs structural key failures instead of aborting the server. Repair the data and remove the switch before normal operation; mapping-drift warnings never abort startup. See Asset Namespaces.
SQLITE_VEC_EXTENSION_PATH—Path to vec0.so native extension for sqlite-vec vector search. Set automatically in the Docker image (/app/extensions/vec0.so). Only needed for non-Docker deployments.
SCRIPT_EXECUTORnativeHow the API server runs scripts. native spawns a sandboxed bun subprocess per run. quickjs (experimental) runs scripts in QuickJS (WASM) inside a pool of worker threads, with ~1 ms per-run overhead (against ~10 ms for native on Linux) and a smaller API surface. See Scripts runtime § Executors.
SCRIPT_QUICKJS_POOL_SIZE4Maximum QuickJS worker threads, an integer from 1 to 32. Read when the shared pool is first created; restart the API after changes. More workers trade memory for concurrent throughput. Separate from SCRIPT_RUN_CONCURRENCY_CAP.
SCRIPT_EGRESS_ALLOW_PRIVATEfalsequickjs executor only: true lets scripts fetch private, loopback, link-local and metadata addresses. Default false refuses them after DNS resolution and on every redirect hop. The native executor does not enforce it yet. See Scripts runtime § Egress policy.
SCRIPT_EGRESS_ALLOWLIST—quickjs executor only: comma-separated host[:port] patterns (a leading *. matches subdomains). When set, scripts can fetch only these hosts, public or private. Empty means any public host. The native executor does not enforce it yet.
SCRIPT_RUNTIME_DIR—Directory holding the pre-built script-runtime bundles (eval-harness.bundle.js, stdlib.bundle.js, swarm-sdk.bundle.js, zod.bundle.js) and the zod-esm/ sources that the quickjs executor bundles from. Set automatically in the Docker image (/app/scripts-runtime). Required by the scripts runtime when running as a compiled binary, since the harness subprocess cannot read the binary's /$bunfs/ virtual filesystem.
TS_LIB_DIR—Directory holding the TypeScript lib.*.d.ts files used by script typecheck. Set automatically in the Docker image (/app/typescript-lib). Required in compiled-binary mode so the TypeScript compiler can resolve the default lib.
MIGRATIONS_DIR—Directory for packaged .sql migration files in compiled-binary mode. It is selected explicitly when import.meta.dir resolves inside Bun's /$bunfs/ virtual filesystem, even if that directory can be read. Set automatically in the Docker image (/app/migrations); a missing or empty directory now stops a fresh database from booting without its baseline schema. Only needed for non-Docker compiled deployments.
SCRIPT_TYPES_DIR—Directory whose node_modules holds the type declarations for the scripts SDK's bare imports (currently zod). Set automatically in the Docker image (/app/script-types). Required for script typecheck to resolve zod in compiled-binary mode, since the binary doesn't ship node_modules.
SCRIPT_WORKFLOW_RUNTIME_DIR—Directory holding the pre-built script-workflow harness bundle (harness.bundle.js), needed by durable script-run subprocesses. Set automatically in the Docker image (/app/script-workflows-runtime). Required when running as a compiled binary.
MODELSDEV_CACHE_PATH—Override path to the vendored modelsdev-cache.json pricing/model snapshot, checked before the built-in candidate paths. Only needed for non-standard deployment layouts; boot-time pricing fallback only (live updates are owned by the pricing-refresh job).
SECRETS_ENCRYPTION_KEYAuto-generated on first bootMaster key for encrypting swarm_config secret rows at rest. Accepts base64 (43-char, e.g. openssl rand -base64 32) or 64-char hex (e.g. openssl rand -hex 32). Decodes to exactly 32 bytes. Reserved: cannot be stored in the DB config store. See Encryption Key.
SECRETS_ENCRYPTION_KEY_FILE—Alternative to SECRETS_ENCRYPTION_KEY: absolute path to a file whose contents are the base64- or hex-encoded key. Useful with Docker secrets or k8s Secret volume mounts.
SCHEDULER_INTERVAL_MS10000Polling interval for scheduled tasks (ms)
SCRIPT_RUN_CONCURRENCY_CAP10Maximum number of concurrently active script runs the API server allows. New run requests are rejected with HTTP 429 once the cap is reached.
SCRIPT_RUN_SUPERVISOR_DISABLEfalseWhen true, disables the background script-run supervisor (subprocess spawn, periodic reconcile, and abort-on-limit) — durable script runs will never be started or reconciled.
SCRIPT_WORKFLOW_DEBUGfalseWhen true, logs verbose debug output (auth override flag, resolved API key length) each time the script-run supervisor spawns a durable script-workflow subprocess.
WORKFLOW_MAX_ITERATIONS100Max times a single workflow node may execute within one run before the engine aborts with an infinite-loop circuit-breaker error.
WORKFLOW_MAX_STEPS_PER_RUN500Max total steps (across all nodes) allowed in a single workflow run before the engine's circuit breaker aborts it.
OPENAI_API_KEY—Fallback key for memory embeddings (used when EMBEDDING_API_KEY is unset).
EMBEDDING_API_KEY—API key for the embedding provider. Takes precedence over OPENAI_API_KEY.
EMBEDDING_API_BASE_URL—Optional custom OpenAI-compatible base URL (e.g. Azure OpenAI, Together, vLLM, Ollama). Leave unset to hit api.openai.com. Azure / Microsoft Foundry: https://<resource>.services.ai.azure.com/openai/v1 (see Embedding endpoints).
EMBEDDING_MODELtext-embedding-3-smallEmbedding model slug sent to the provider. On Azure, the embedding deployment name.
EMBEDDING_DIMENSIONS512Vector dimensions requested from the model. Must match what the model supports.
MEMORY_RECENCY_HALF_LIFE_DAYS14Optional global override for memory recency decay. When unset, recency defaults are source-aware: manual memories do not decay, file_index uses 180 days, task_completion uses 14 days, and session_summary uses 7 days.
MEMORY_HYBRID_SEARCH1 (on)Hybrid memory retrieval blends vector similarity with a full-text pass before reranking. Set to 0 or false to use vector-only search.
MEMORY_GRAPH_EXPANSION1 (on)Expands search candidates with 1-hop memory-link neighbors (resolved [[wikilink]] targets) before reranking. Neighbors are damped (parentRawSimilarity × strength × 0.7), capped at 5 per search, respect the caller's scope/source/lead ACL, and carry retrievalSource: "graph" so GET /api/memory/usefulness can measure the arm's citation rate. Set to 0 or false to disable expansion.
MEMORY_MIN_SIMILARITY0.1Minimum raw cosine similarity required before a memory candidate survives reranking. Filters low-relevance noise before recency/access boosts are applied.
MEMORY_ACCESS_BOOST_MAX1.5Maximum access boost multiplier for reranking
MEMORY_ACCESS_RECENCY_HOURS48Hours within which access counts for full boost
MEMORY_CANDIDATE_MULTIPLIER3Candidate set size relative to requested limit
MEMORY_DEMOTION_FLOOR1.0Floor for the Beta-Binomial usefulness multiplier applied during memory reranking (paired with MEMORY_RATERS). Default 1.0 disables demotion entirely; lower values (e.g. 0.5) let poorly-rated memories sink below neutral relevance.
OPENROUTER_API_KEY—Required for the Stop-hook session summarizer + LLM memory rater (calls go through the Vercel AI SDK against OpenRouter). When unset, both session-summary indexing and the llm rater are no-ops
OPENROUTER_BASE_URLhttps://openrouter.ai/api/v1Optional OpenAI-compatible gateway for every OpenRouter consumer, including harness sessions, credential checks, memory/session summarizers, and raw-LLM or validation workflow nodes. The target is not required to be openrouter.ai — any gateway serving OpenRouter-compatible GET /models and POST /chat/completions works (see Model Gateways). Trailing slashes are removed. Leave unset or blank for direct OpenRouter traffic. Also settable from Settings → Configuration.
OPENROUTER_APP_ATTRIBUTIONtrueSends OpenRouter's app attribution headers (HTTP-Referer, X-OpenRouter-Title, X-OpenRouter-Categories) on requests that go straight to an openrouter.ai host, so the usage counts toward the Agent Swarm app. Gateways set through OPENROUTER_BASE_URL never receive them. Set to false or 0 to opt out. Read from each task's resolved config (see App attribution). Also settable from Settings → Configuration.
MEMORY_RATERSimplicit-citation,explicit-selfComma-separated list of memory raters to enable (noop, implicit-citation, explicit-self, llm). Gates the server-side rating pass fired after task completion (implicit-citation) and worker-side gates: the Stop-hook's LLM-rating piggyback (llm) and the explicit self-rating tool hint (explicit-self). Unknown names are logged and skipped; unset enables citation ratings and self-rating hints on new and existing deployments. Set MEMORY_RATERS= explicitly to disable all raters. Deleting a swarm config override restores the deployment value, or the runtime default when unset.
MEMORY_RATER_MODELPinned per credential kind (MEMORY_RATER_DEFAULT_MODEL): openrouter/deepseek/deepseek-v4.1-flash, anthropic/claude-haiku-4-5, openai/gpt-6-luna, openai-codex/gpt-6-luna, or haiku (Claude CLI)Overrides the resolved model slug for the Stop-hook session-summarizer + LLM memory rater, across whichever credential (OpenRouter/Anthropic/OpenAI/Codex OAuth/Claude CLI) the shared internal-ai abstraction resolves. The defaults are pinned for the rater only: they do not follow the general default models (e.g. workflow LLM nodes), so a bump there never swaps the judge. Each llm rating records the model that produced it in memory_rating.model. On the OpenRouter path completeStructured sends tool_choice: "required", so the model must accept forced tool calls: OpenRouter answers 400 for anthropic/claude-sonnet-5.5 (tool_choice: type tool and any are not supported) and for qwen/qwen3.8-flash, so neither can be the rater model there.
MEMORY_RATER_WEIGHTS1.0 per raterOptional name:multiplier,... overrides (e.g. llm:0.5,implicit-citation:2) applied to each rater's emitted rating weight before it's persisted. Unlisted raters use 1.0; the final per-event weight is clamped to [0, 1].
CAPABILITIEScore,task-pool,scripts,config,mcp,profiles,scheduling,memory,workflows,pages,metrics,kv,slack,tracker,skills,repoComma-separated capability flags gating which MCP tool groups the API server registers. Disabled by default: prompt-templates, messaging, swarm-x, agentmail, kapso. Setting this replaces the default list (not additive) — include every capability you want. Can also be set as a global swarm-config entry, which takes precedence over the env var at server creation. Upgrade note: when an explicit env value is set and no swarm-config CAPABILITIES row exists, boot auto-seeds a row backfilling the previously always-registered groups (core, config, scripts, mcp, slack, tracker, skills, repo) so legacy lists don't silently lose those tools — edit or delete the seeded row to take full control. Note: this shapes the externally exposed MCP tool list only — it is not a feature kill-switch. The scripts SDK bridge always sees the full tool surface (governed by its own allowlist), and HTTP REST routes are generally not gated. See MCP tools reference for the tool-to-capability mapping.
SWARM_ENABLED_TOOLSunsetOptional MCP tool allowlist: comma-separated tool names (or a JSON array). When set, the API server registers exactly these tools on each agent MCP session, ignoring CAPABILITIES and SCRIPTS_ONLY_MCP. Unknown names are logged once and skipped; a blank or empty list is logged and ignored. Agent-scoped config beats global config, which beats this env var; changes apply on the next MCP session with no restart. The scripts SDK bridge is unaffected. See Agent capabilities and tool allowlist.
SKILL_FILES_MAX_COUNT100Max number of files allowed in a single skill file-set upsert.
SKILL_FILES_MAX_FILE_BYTES512000 (500 KB)Max size in bytes for a single file within a skill file-set upsert.
SKILL_FILES_MAX_TOTAL_BYTES10485760 (10 MB)Max combined size in bytes for all files in a single skill file-set upsert.
BUDGET_ADMISSION_DISABLEDfalseOperator escape hatch — set to true at process boot to make budget admission always return allowed, bypassing the agent/global/user daily-spend budget gates on task claiming.
MULTI_RUNTIME_ENABLEDtrueLets several worker processes serve one logical agent. Set consistently on the API server and every worker; runtimes sharing an AGENT_ID need compatible workspace state. The agent-scoped AGENT_MAX_TASKS setting remains the shared logical task limit.
API_DRAIN_MAX_MS0Max wait (ms) for workers to hand off in-flight tasks after the API gets SIGTERM, while it still serves and dispatches nothing. Off by default (0, also for an unset, empty, or invalid value); set it for deploys that stop the API and the workers together. While it is on, an API-only restart also makes live workers hand off. Max 120000. Keep the API stop grace period above this value.
HEARTBEAT_INTERVAL_MS90000Heartbeat sweep interval (ms)
HEARTBEAT_CHECKLIST_DISABLEfalseSet to true or 1 to disable the periodic HEARTBEAT.md checklist polling loop and the boot-triage task (separate from the infrastructure sweep). false and 0 keep it on.
HEARTBEAT_BOOT_TRIAGE_ALWAYSfalseSet to true or 1 to create the boot-triage task on every restart. By default a clean boot (no interrupted, stalled, orphaned, or resume-less superseded tasks) only logs a one-line note and creates no Lead task.
HEARTBEAT_CHECKLIST_GATE_SCRIPT-Name of a global-scope script run (as the Lead) before each recurring checklist task. One 60 s deadline covers lookup, credential setup, and execution. It returns { quiet: boolean, reason?: string, summary?: string }. A failure, a timeout, or any other shape counts as an error verdict and never skips a task. Unset runs the built-in heartbeat-jev-gate when a global TYPESAFE_API_KEY or OPENROUTER_API_KEY is set, and is off otherwise. off or an empty value turns the gate off.
HEARTBEAT_CHECKLIST_GATE_MODEshadowshadow creates every checklist task and tags it heartbeat-gate:quiet, heartbeat-gate:wake, or heartbeat-gate:error, so you can compare the verdict with the Lead's outcome. enforce skips the task on a quiet verdict and appends the gate's summary to the task on a wake verdict.
HEARTBEAT_JEV_GATE_PROVIDERautoWhich global key the built-in heartbeat-jev-gate uses: typesafe (TYPESAFE_API_KEY), openrouter (OPENROUTER_API_KEY), or auto for whichever is set. An OPENROUTER_API_KEY behind a custom OPENROUTER_BASE_URL does not count, so it does not turn the default gate on.
HEARTBEAT_CHECKLIST_INTERVAL_MS1800000Interval (ms) between HEARTBEAT.md checklist checks (default 30 min). 0 turns the recurring check off; boot triage still runs.
HEARTBEAT_DISABLEfalseSet to true to disable the heartbeat module
HEARTBEAT_STALL_THRESHOLD_MIN30Minutes before an in-progress task is considered stalled
HEARTBEAT_STALL_NO_SESSION_MIN5Minutes before an in-progress task with no active worker session is considered stalled
HEARTBEAT_STALL_STALE_HB_MIN15Minutes before an in-progress task with a stale worker heartbeat is considered stalled
HEARTBEAT_STALE_CLEANUP_MIN30Minutes before stale resources (sessions, reviewing tasks) are cleaned up
HEARTBEAT_MAX_AUTO_ASSIGN5Max pool tasks to auto-assign per sweep
HEARTBEAT_MAX_RESUME_GENERATIONS3Max crash-recovery resume generations for a task before it is failed for Lead triage instead of resumed again
HEARTBEAT_PIN_CRASH_RESUMEtrue (on)Rollback kill-switch for pinning crash_recovery resumes back to their original agent. Set to 0 to restore the pre-pin behavior requiring the WORKER_LIVENESS_WINDOW_SECONDS freshness check
HEARTBEAT_PIN_GRACEFUL_RESUMEtrue (on)Rollback kill-switch for pinning graceful_shutdown resumes back to their original agent. Set to 0 to restore the old pool-based follow-up path
HEARTBEAT_RESUME_PIN_GRACE_MIN10Grace window (minutes) a same-agent-pinned crash-recovery or graceful-shutdown resume waits before the reaper escalates it to a Lead re-delegation decision. Set to 0 to disable the reaper
RUNTIME_STALE_THRESHOLD_MIN5Minutes without runtime traffic before an active runtime stops counting and the heartbeat sweep retires it. Used only when MULTI_RUNTIME_ENABLED is on.
RUNTIME_POOLS_ENABLEDfalseEnables runtime pools (beta): push-based, on-demand workers that the API starts through Docker or E2B controllers. Keep MULTI_RUNTIME_ENABLED=true too. See Runtime pools for the full list of RUNTIME_* tuning variables
RUNTIME_DEFAULT_DELIVERYpollDelivery mode (poll or push) for pool workers whose pool does not set one
RUNTIME_WAKE_GRACE_SEC15Seconds a task waits for an awake agent before routing can assign it to an eligible sleeping pool agent
RUNTIME_PROVISION_CONCURRENCY8Maximum runtimes the supervisor provisions at once
RUNTIME_WORKER_API_URL-API origin that pool workers use to reach the swarm, when it differs from the public API URL
RUNTIME_DOCKER_HOST-Docker socket seeded into the built-in controller, even when Docker is offline. Falls back to DOCKER_HOST, then unix:///var/run/docker.sock
APPROVAL_REQUEST_AUTO_CANCELLATION_DAYS7Days a pending approval request with no explicit timeout waits before the heartbeat cancels it. A request that gates a running or waiting workflow run cancels that run too. Set to 0 to disable
MODEL_LATEST_SOAK_DAYS2Days a newly released model must age before a latest:<provider>/<target>@stable model-tier alias can resolve to it. 0 disables the soak. @any aliases ignore it
MODEL_AUTO_UPGRADEtrueSet to false or 0 to freeze each latest: model-tier alias at its last recorded resolution (an alias never resolved before still resolves once). See Model tiers
WORKER_LIVENESS_WINDOW_SECONDS30Seconds within which a worker's last-activity timestamp must be fresh to be treated as "online" for resume pre-assignment / same-agent crash-pin freshness checks

Encryption key resolution (see Encryption Key for the full guide):

  1. SECRETS_ENCRYPTION_KEY env var
  2. SECRETS_ENCRYPTION_KEY_FILE env var (path to a file containing the key)
  3. <data-dir>/.encryption-key on the API's data volume
  4. Auto-generated on first boot only when the DB does not yet contain encrypted secret rows

Losing the key while keeping the database means losing every encrypted secret with no recovery path. Back up the key alongside every database backup.

Docker Worker Variables

Harness Provider

VariableRequiredDefaultDescription
HARNESS_PROVIDERNoclaudeAI provider: claude (Claude Code), codex, opencode, pi (pi-mono), devin (Devin), or claude-managed (Anthropic Managed Agents). When unset and the worker has no Anthropic credential but has an OpenRouter, NaN or Helmcode key, the default is pi. See Harness Configuration

Credentials

Which credentials you need depends on your selected harness provider:

Claude Code (HARNESS_PROVIDER=claude, default):

VariableRequiredDefaultDescription
CLAUDE_CODE_OAUTH_TOKENYes*—OAuth token for Claude CLI. Supports comma-separated values for multi-credential load balancing
ANTHROPIC_API_KEYAlt*—Alternative to CLAUDE_CODE_OAUTH_TOKEN. Also supports comma-separated values
SWARM_USE_CLAUDE_BRIDGENofalseReloadable boolean (true/1 enables, false/0/unset disables). When enabled, the Claude adapter routes through the installed claude-bridge binary from pinned package @desplega.ai/claude-bridge@0.2.2, a Desplega-owned claude -p drop-in that drives interactive Claude Code through tmux. Requires Bun, claude, and tmux on PATH. Claude Bridge requires CLAUDE_CODE_OAUTH_TOKEN; if only ANTHROPIC_API_KEY is present, the adapter logs a warning and falls back to stock claude. See Claude Bridge.
CLAUDE_BINARYNoclaudeLow-level argv prefix for the Claude CLI. Accepts a single binary name (claude), an absolute path, or a whitespace-separated command string. Legacy bridge commands remain supported only as a deprecated compatibility path; prefer SWARM_USE_CLAUDE_BRIDGE=true. Reloadable via swarm_config (precedence: repo > agent > global > env > claude) so you can flip a worker via set-config CLAUDE_BINARY=... without a container restart.
ENABLE_PROMPT_CACHING_1HNo1Enables Anthropic's 1-hour prompt cache for every Claude Code session spawned by the worker. Default-on since v1.69.1; set to 0 (env or swarm_config) to opt out. Injected by ClaudeAdapter into the spawned claude process env.

* One of CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY is required.

pi-mono (HARNESS_PROVIDER=pi):

VariableRequiredDefaultDescription
ANTHROPIC_API_KEYOne of*—Anthropic API key for Claude models via pi-mono
OPENROUTER_API_KEYOne of*—OpenRouter API key for multi-provider model access
GOOGLE_API_KEY or GEMINI_API_KEYWhen MODEL_OVERRIDE starts with google/—Google AI API key for direct Google models. Either name is accepted
NAN_API_KEYOne of*—NaN API key for nan/<id> models (pi and opencode only). HELMCODE_API_NAN is accepted as an alias; NAN_API_KEY wins when both are set. Supports comma-separated values for multi-credential load balancing. See NaN and Helmcode
HELMCODE_API_KEYOne of*—Helmcode API key for helmcode/<id> models (pi and opencode only). Supports comma-separated values for multi-credential load balancing. See NaN and Helmcode
NAN_BASE_URLNohttps://api.nan.builders/v1Overrides the NaN API root for pi, opencode and the key check. Not a secret. Every registry provider reads <ID>_BASE_URL the same way
HELMCODE_BASE_URLNohttps://api.helmcode.com/v1Overrides the Helmcode API root for pi, opencode and the key check. Not a secret
OPENROUTER_BASE_URLNohttps://openrouter.ai/api/v1OpenAI-compatible gateway used for OpenRouter model and summarizer traffic. Any chat-completions gateway works — see Model Gateways
BEDROCK_AUTH_MODENoinferred from MODEL_OVERRIDEOptional Bedrock mode override. sdk uses the AWS SDK credential chain; bearer requires AWS_BEARER_TOKEN_BEDROCK. Both probe credentials and enumerate models
AWS_BEARER_TOKEN_BEDROCKIn Bedrock bearer mode—Bedrock API key required when BEDROCK_AUTH_MODE=bearer
AWS_REGIONIn either Bedrock mode—Required region for Bedrock credential probing and account-accurate model enumeration in SDK or bearer mode
PI_TOOL_DEFERRALNofalseReloadable boolean. Hides non-core swarm tools behind pi's tool_search. See pi tool deferral
PI_CODEMODENofalseReloadable boolean. Adds pi's codemode tool, a bounded QuickJS sandbox in which the model's JavaScript calls swarm tools. See pi codemode
PI_CODEMODE_MODELSNofalseReloadable boolean, effective only with PI_CODEMODE. Lets codemode scripts call pi's models API (classify(), generateImages()); the usage counts toward session cost. See pi codemode

* At least one credential source is required (API key, ~/.pi/agent/auth.json, or the AWS SDK default chain in Bedrock SDK mode). Do not pass CLAUDE_CODE_OAUTH_TOKEN when using pi-mono — it will override the configured provider. See Harness Configuration and Harness Providers for details.

Codex (HARNESS_PROVIDER=codex):

VariableRequiredDefaultDescription
OPENAI_API_KEYOptional—Standard OpenAI API key for Codex
API_KEYYes—Swarm API key used to fetch codex_oauth from the config store
MCP_BASE_URLYeshttp://host.docker.internal:3013Swarm API URL reachable by the worker
CODEX_SKILLS_DIRNo~/.codex/skillsDirectory used by the Codex inline slash-command resolver to find <name>/SKILL.md (mirrors OPENCODE_SKILLS_DIR)
CODEX_PATH_OVERRIDENoSet automatically in the Docker image (/usr/bin/codex)Absolute path to the Codex CLI wrapper or binary. The app-server transport invokes this path with app-server --listen stdio://. Use it for non-Docker or custom installs.
AGENT_SWARM_CODEX_RUNNER_ARGVNoInferred from process.argv (dev vs. compiled-binary layout)JSON-encoded string array overriding the argv prefix used to re-launch the codex-session-runner subprocess. Niche escape hatch for nonstandard install/packaging layouts
NODE_EXTRA_CA_CERTSNo—Path to an extra CA certificate bundle, forwarded to the spawned Codex CLI subprocess so it trusts custom/corporate CAs on outbound HTTPS calls

Codex can also authenticate through ~/.codex/auth.json, including ChatGPT OAuth restored from the swarm config store. See Provider Auth: Codex OAuth.

Amp (HARNESS_PROVIDER=amp):

VariableRequiredDefaultDescription
AMP_API_KEYYes—Amp access token (sgamp_...) from ampcode.com Settings → Security. The short-lived amp login session token is rejected
AMP_BINARYNoamp on PATHPath to another trusted Amp CLI executable

See Provider Auth: Amp for the token, modes and cost tracking.

Grok (HARNESS_PROVIDER=grok):

VariableRequiredDefaultDescription
XAI_API_KEYOne of the two—xAI API key from console.x.ai, for xAI models. SuperGrok subscription logins are not supported
OPENROUTER_API_KEYOne of the two—OpenRouter key, for openrouter/<id> models
GROK_BINARYNogrok on PATHPath to another trusted Grok CLI executable
GROK_MODELS_BASE_URL, GROK_MODELS_LIST_URL, GROK_XAI_API_BASE_URLNo—The Grok CLI's endpoint overrides, passed through on the xAI route

See Provider Auth: Grok for models, isolation and cost tracking.

opencode (HARNESS_PROVIDER=opencode):

VariableRequiredDefaultDescription
OPENROUTER_API_KEYOne of*—OpenRouter API key (primary — unlocks 100+ models at openrouter.ai)
OPENROUTER_BASE_URLNohttps://openrouter.ai/api/v1OpenAI-compatible gateway used by the OpenRouter provider, model refreshes, and the session summarizer plugin. Any chat-completions gateway works — see Model Gateways
ANTHROPIC_API_KEYOne of*—Anthropic API key for Claude models
OPENAI_API_KEYOne of*—OpenAI API key for GPT models
NAN_API_KEYOne of*—NaN API key for nan/<id> models (pi and opencode only). HELMCODE_API_NAN is accepted as an alias; NAN_API_KEY wins when both are set. Supports comma-separated values for multi-credential load balancing. See NaN and Helmcode
HELMCODE_API_KEYOne of*—Helmcode API key for helmcode/<id> models (pi and opencode only). Supports comma-separated values for multi-credential load balancing. See NaN and Helmcode
NAN_BASE_URLNohttps://api.nan.builders/v1Overrides the NaN API root for pi, opencode and the key check. Not a secret. Every registry provider reads <ID>_BASE_URL the same way
HELMCODE_BASE_URLNohttps://api.helmcode.com/v1Overrides the Helmcode API root for pi, opencode and the key check. Not a secret
OPENCODE_SDK_PATHNoGlobal npm rootAbsolute path to @opencode/sdk/dist/index.js, when it is not under /usr/lib/node_modules or /usr/local/lib/node_modules
OPENCODE_SKILLS_DIRNo~/.opencode/skillsDirectory used by the OpenCode inline resolver for /<skill> prompts
MODEL_OVERRIDENoopenrouter/qwen/qwen3-coder-flashModel string passed to opencode. Use provider/model-id format (e.g. anthropic/claude-sonnet-4-6, openai/gpt-4o)

* At least one of OPENROUTER_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, NAN_API_KEY (or HELMCODE_API_NAN), HELMCODE_API_KEY, or ~/.local/share/opencode/auth.json is required.

Devin (HARNESS_PROVIDER=devin):

VariableRequiredDefaultDescription
DEVIN_API_KEYYes—Devin API key (prefix: cog_*)
DEVIN_ORG_IDYes—Devin organization ID (prefix: org-*)
DEVIN_POLL_INTERVAL_MSNo15000Polling interval for Devin session events (ms)
DEVIN_ACU_COST_USDNo2.25USD per ACU for cost tracking
DEVIN_API_BASE_URLNohttps://api.devin.aiDevin API base URL (override for testing)
DEVIN_MAX_ACU_LIMITNo—Per-session ACU cap (sent to Devin API + UI budget bar)
MAX_SKILL_CHARSNo100000Max chars for inlined SKILL.md content
HAS_MCPConditionalfalseDeclares whether the Devin environment/session has MCP tool access. true is not yet supported and throws at session creation; a Devin worker configured with AGENT_ROLE=lead also requires MCP access, since a lead needs the MCP to function
DEVIN_SKILLS_DIRNo<project-root>/plugin/pi-skillsDirectory used by the Devin inline @skills:<name> resolver to find SKILL.md files

Starter env file: .env.docker-devin.example at the repo root.

Claude Managed Agents (HARNESS_PROVIDER=claude-managed):

VariableRequiredDefaultDescription
ANTHROPIC_API_KEYYes—Anthropic API key (used by both the setup CLI and the runtime adapter)
MANAGED_AGENT_IDYes—Anthropic Agent ID. Written by claude-managed-setup
MANAGED_ENVIRONMENT_IDYes—Anthropic Environment ID. Written by claude-managed-setup
MCP_BASE_URLYes—Must be HTTPS-public so Anthropic's sandbox can reach /mcp. Worker fails fast at boot otherwise
MANAGED_AGENT_MODELNoclaude-sonnet-5Default model on sessions.create; per-task task.model overrides
MANAGED_GITHUB_VAULT_IDNo—Anthropic vault ID holding a GitHub PAT, for repo-bound tasks (recommended for prod)
MANAGED_GITHUB_TOKENNo—Literal GitHub PAT injected as authorization_token on github_repository resources (dev-only fallback)
MANAGED_MCP_VAULT_IDNo—Anthropic vault ID holding the static-bearer credential for the swarm's /mcp endpoint, passed via vault_ids on sessions.create alongside MANAGED_GITHUB_VAULT_ID. Written by claude-managed-setup and auto-restored from swarm_config at worker boot
CLAUDE_MANAGED_RUNTIME_FEE_USD_PER_HOURNo0.08USD-per-session-hour runtime fee used by the local cost snapshot shown in worker logs before the API server's canonical pricing table recomputes the final cost. Override for ops pricing bumps without a redeploy

Run the one-time bunx @desplega.ai/agent-swarm claude-managed-setup from your laptop to bootstrap the Anthropic-side Agent + Environment and persist IDs into swarm_config. See Harness Configuration § Claude Managed Agents.

General Worker Settings

VariableRequiredDefaultDescription
API_KEYYes—API key for MCP server
AGENT_IDNoAuto-generatedAgent UUID. Keep stable for task resume
AGENT_ROLENoworkerRole: worker or lead
AGENT_NAMENoAuto-generatedDisplay name for the agent
MCP_BASE_URLNohttp://host.docker.internal:3013MCP server URL
WORKER_API_READY_TIMEOUT_SECONDSNo90Positive-integer deadline for docker-entrypoint.sh to reach ${MCP_BASE_URL}/health before provider-specific setup. This is bootstrap-only: set it in the worker/lead container environment rather than only in swarm_config.
SESSION_IDNoAuto-generatedLog folder name
LOG_DIRNo/logsBase directory for session .jsonl log files (<LOG_DIR>/<sessionId>/<timestamp>-<taskId8>.jsonl)
YOLONofalseContinue on errors
SYSTEM_PROMPTNo—Custom system prompt text
SYSTEM_PROMPT_FILENo—Path to system prompt file
CONTEXT_MODE_DISABLEDNofalseSet to true to turn context-mode off for local Claude Code, Codex, and opencode workers. Drops the ctx_* MCP server and disables the installed Claude Code and Codex context-mode plugins for the session, so their hooks (SessionStart guidance, tool tips, WebFetch redirect) do not run either.
SCRIPTS_ONLY_MCPNofalseExperimental code-mode. Set alongside the API server value so worker prompts explain the reduced eight-tool MCP surface; all other swarm operations remain available through script-run and the scripts SDK.
CONTEXT_MODE_EXTERNAL_MCP_NUDGE_EVERYNo3How often (in turns) the context-mode MCP plugin surfaces its external-MCP guidance nudge, across Claude Code, Codex, and opencode alike
CONTEXT_PREAMBLE_MAX_TOKENSNo2000Token budget for the universal follow-up context preamble prepended to a child task's prompt; applies across all harness providers
CONTEXT_PREAMBLE_RESUME_MAX_TOKENSNo4000Token budget for the resume-task preamble (2x the regular budget — carries the original task brief plus a tool-call summary so the resumed agent doesn't redo completed work)
STARTUP_SCRIPT_STRICTNofalseExit on startup script failure. By default, per-agent setupScript failures log a v1.106.0 privilege-boundary migration warning and the worker continues booting
SHUTDOWN_TIMEOUTNo30000Grace period (ms) before pausing tasks
MAX_CONCURRENT_TASKSNo1Maximum parallel tasks per worker
SWARM_DELIVERY_MODENoPool settingpoll or push. Overrides the delivery mode a runtime pool worker gets at registration. Pool supervisors set it on the workers they start. See Runtime pools
SWARM_URLNolocalhostBase domain for service URLs
LEAD_PORTNo3020Host port for lead service. Example variable used in docker-compose.example.yml — adjust to your setup. In isolated network namespaces all services can share the same port.
WORKER1_PORTNo3021Host port for worker-1 service. Example — see LEAD_PORT.
WORKER2_PORTNo3022Host port for worker-2 service. Example — see LEAD_PORT.
TEMPLATE_IDNo—Template for initial profile on first boot (e.g., official/coder)
TEMPLATE_REGISTRY_URLNohttps://templates.agent-swarm.devURL of the templates registry
MODEL_TIER_MAPNo—JSON object overriding portable tier-to-model resolution for the current worker, for example {\"smol\":\"gpt-5.4-mini\",\"smart\":\"gpt-5.5\"}. Applies when a task, schedule, or workflow uses modelTier.
MODEL_TIER_<TIER>No—Per-tier override for smol, regular, smart, or ultra (for example MODEL_TIER_SMART=gpt-5.5). Wins over MODEL_TIER_MAP.
SWARM_DEP_POSTGRES_ENABLEDNofalseSet to true to start the optional bundled PostgreSQL 16 cluster on worker boot. The root-stage entrypoint runs scripts/init-local-postgres.sh before privilege drop; binaries ship dormant in the worker image since v1.93.0
LOCAL_POSTGRES_DATA_DIRNo/tmp/postgres-dataData directory for the optional local Postgres cluster
LOCAL_POSTGRES_PORTNo5433Port for the optional local Postgres cluster
LOCAL_POSTGRES_USERNopostgresSuperuser name for the optional local Postgres cluster
LOCAL_POSTGRES_PASSWORDWhen local Postgres is enabledNoneExplicit non-empty superuser password; applied to new and existing clusters on each helper invocation
LOCAL_POSTGRES_DBNoappDatabase created in the optional local Postgres cluster

Git Configuration

VariableDefaultDescription
GITHUB_TOKEN—GitHub token for git operations
GITHUB_EMAILworker-agent@desplega.aiGit commit email
GITHUB_NAMEWorker AgentGit commit name

Slack Integration

VariableDescription
SLACK_MODETransport selection: socket (default) or http. Invalid values fail closed. HTTP is configuration-only until its receiver is installed and never falls back to Socket Mode.
SLACK_BOT_TOKENBot User OAuth Token (xoxb-...), required in both modes
SLACK_APP_TOKENApp-Level Token for Socket Mode (xapp-...), required only in socket mode
SLACK_SIGNING_SECRETSigning Secret, required only in http mode
SLACK_DISABLESet to true to disable Slack
SLACK_ALLOW_DEV_SOCKET_MODESet to true to explicitly allow a NODE_ENV=development API process to connect through Socket Mode (default: false)
SLACK_RENDER_V2One persistent task tree per physical Slack thread plus streamed, immutable outcome cards (default: true; set false to opt out)
SLACK_WORK_OBJECTS_ENABLEDSet to true to populate task Work Object flexpanes (default: false). Apply the updated Slack manifest, reinstall the app, and enable Work Object Previews. See Slack setup.
SLACK_RENDER_V2_DELEGATIONSet to true to defer an ask's conclusion card until its whole closure (delegated tasks, children, follow-ups) settles, post child result cards, and gate the checkmark reaction on the closure instead of the ask alone. Requires SLACK_RENDER_V2=true (default: false)
SLACK_CONCLUSION_SETTLE_SECQuiet seconds after a closure goes fully terminal before its conclusion card posts (default: 10)
SLACK_CONCLUSION_TIMEOUT_MINIdle minutes before a closure concludes with unfinished work and a warning reaction (default: 240)
SLACK_TREE_STALL_MINMinutes without a task update before the thread tree shows a stalled glyph for that task (default: 15)
SLACK_STATUS_ICON_COMPLETED"Done" glyph in thread text: task tree, thread header and completed outcome cards. Bare name or :name:, rendered as :name:. Unset or invalid keeps ✅. A name the workspace lacks shows as literal :name:. See Slack: Acceptance reactions
SLACK_ALLOWED_EMAIL_DOMAINSComma-separated email domains
SLACK_ALLOWED_USER_IDSComma-separated user IDs to always allow
ADDITIVE_SLACKSet to true to enable non-mention thread message buffering and batching
ADDITIVE_SLACK_BUFFER_MSDebounce window for thread buffer in ms (default: 10000)
SLACK_THREAD_FOLLOWUP_REQUIRE_MENTIONSet to true to require @mention for thread follow-up routing (default: false)
LEAD_MONITOR_CHANNELSSet to true to have the lead agent additionally poll monitored Slack channels for non-mention activity (not just @mentions/DMs), throttled to once per 60s per poll cycle
LEAD_MONITOR_CHANNEL_IDSComma-separated Slack channel IDs allowlist restricting LEAD_MONITOR_CHANNELS activity monitoring to specific channels. Unset monitors every channel the bot is a member of
SLACK_BLOCK_EXTERNAL_CHANNELSSet to true to keep the bot out of external Slack Connect channels: no tasks from them, no writes to them, and the bot leaves on join or share (default: false). Read on every event, so a change in global config applies without a restart. See Slack: External channels
SLACK_ALERTS_CHANNELSlack channel ID/name for setup-blocked automation, OAuth/Jira keepalive failures, claimable-queue stall/recovery alerts, and external Slack Connect channel alerts. Repeated identical schedule setup failures are suppressed within a UTC day; workflow setup alerts are limited to one per workflow per UTC day. Unset means alerts are logged only, not sent

GitHub Integration

VariableDescription
GITHUB_WEBHOOK_SECRETWebhook secret for GitHub App
GITHUB_BOT_NAMEBot name for @mentions (default: agent-swarm-bot)
GITHUB_BOT_ALIASESComma-separated additional @mention aliases (e.g. heysidekick,sidekick)
GITHUB_EVENT_LABELSComma-separated labels that trigger agent action on PR/issue label events (default: swarm-review)
GITHUB_SKIP_NOOP_APPROVALSSet to false to notify the lead on every PR approval, including ones with no body and no open review items (default: true)
GITHUB_APP_IDGitHub App ID (for bot reactions; without an App, reactions fall back to GITHUB_TOKEN)
GITHUB_APP_PRIVATE_KEYGitHub App private key (base64-encoded)
GITHUB_DISABLESet to true to disable GitHub

GitLab Integration

VariableDescription
GITLAB_TOKENGitLab PAT or Group Access Token for API calls
GITLAB_URLGitLab instance URL (default: https://gitlab.com)
GITLAB_WEBHOOK_SECRETShared secret for webhook verification
GITLAB_BOT_NAMEBot username for @mention detection (default: agent-swarm-bot)
GITLAB_EMAILGit commit email for GitLab repos (default: GITHUB_EMAIL, then worker-agent@desplega.ai; not applied when GITHUB_TOKEN is set)
GITLAB_NAMEGit commit name for GitLab repos (default: GITHUB_NAME, then Worker Agent; not applied when GITHUB_TOKEN is set)
GITLAB_DISABLESet to true to disable GitLab integration

Azure DevOps Integration

VariableDescription
AZURE_DEVOPS_TOKENPersonal Access Token (scope: Code, Read & write; add Work Items, Read & write for Azure Boards) for git, az repos, az boards, and API calls
AZURE_DEVOPS_ORG_URLOrganization URL, e.g. https://dev.azure.com/your-org
AZURE_DEVOPS_WEBHOOK_SECRETBasic auth password the service-hook subscriptions send; enables the webhook endpoint
AZURE_DEVOPS_BOT_IDIdentity GUID of the bot user for @<GUID> mentions (default: the PAT owner)
AZURE_DEVOPS_ALLOWED_ACTORSComma-separated sign-in names allowed to trigger Azure Boards work item tasks without a linked swarm user
AZURE_DEVOPS_WORKITEM_STATE_STARTEDWork item state to set when its swarm task starts (process-specific, e.g. Doing); unset = no change
AZURE_DEVOPS_WORKITEM_STATE_DONEWork item state to set when its swarm task completes (e.g. Done); unset = no change
AZURE_DEVOPS_BOT_NAMEName for plain-text @name mentions (default: agent-swarm-bot)
AZURE_DEVOPS_EMAILGit commit email for Azure Repos commits (default: GITHUB_EMAIL, then worker-agent@desplega.ai; not applied when GITHUB_TOKEN or GITLAB_TOKEN is set)
AZURE_DEVOPS_NAMEGit commit name for Azure Repos commits (default: GITHUB_NAME, then Worker Agent; not applied when GITHUB_TOKEN or GITLAB_TOKEN is set)
AZURE_DEVOPS_DISABLESet to true to disable the Azure DevOps integration

AgentMail Integration

VariableDescription
AGENTMAIL_DISABLESet to true to skip AgentMail integration
AGENTMAIL_WEBHOOK_SECRETSvix signing secret for webhook verification
AGENTMAIL_INBOX_DOMAIN_FILTERComma-separated inbox addresses or domains to allow for incoming inbox webhooks (e.g., bot@yourdomain.com,example.com). An entry with @ matches that exact address; a bare domain matches any address on it. Unmatched inboxes are silently dropped
AGENTMAIL_SENDER_DOMAIN_FILTERComma-separated sender addresses or domains to allow (e.g., me@gmail.com,company.com). An entry with @ matches that exact address; a bare domain matches any address on it. Unmatched senders are silently dropped
ADDITIVE_AGENTMAILSet to true to enable non-mention thread message buffering and batching for AgentMail, mirroring ADDITIVE_SLACK
ADDITIVE_AGENTMAIL_BUFFER_MSDebounce window (ms) for buffering rapid AgentMail follow-up messages into a single task when ADDITIVE_AGENTMAIL=true is set (default: 10000)

Kapso / WhatsApp Integration

VariableDescription
KAPSO_API_KEYKapso API key used for outbound sends and optional webhook registration (X-API-Key)
KAPSO_PHONE_NUMBER_IDDefault WhatsApp Business phone-number ID the swarm sends from
KAPSO_WEBHOOK_HMAC_SECRETShared secret used to verify Kapso's X-Webhook-Signature header on inbound webhooks
KAPSO_API_BASE_URLOptional Kapso API base URL override (default: https://api.kapso.ai)

Setup Steps

  1. Set the values above in your .env or integrations dashboard.
  2. Have the lead call register-kapso-number to point the number at /api/integrations/kapso/webhook and store the routing mapping.
  3. Use send-whatsapp-message / reply-whatsapp-message for the common text path, or the kapso-whatsapp skill for templates, media, and reactions.

Chat SDK (Telegram, Slack, Teams, Discord)

VariableDescription
CHAT_SDK_ENABLEDSet to true to start the Chat SDK channel and enable the chat-sdk capability (default: false). A saved change applies within a few seconds
CHAT_SDK_ADAPTERSJSON object keyed by adapter name (telegram, slack, discord, teams). Values may reference secret config rows as {"$config": "KEY"}
CHAT_SDK_BUFFER_MSHow long a thread collects messages before it creates one task (default: 10000, range 1 to 600000)
CHAT_SDK_API_URL_OVERRIDESEnvironment only, for development and tests. JSON of platform API base URLs by adapter name. Never set it in production

A slack adapter requires SLACK_DISABLE=true. See Chat SDK for setup.

Sentry Integration

VariableDescription
SENTRY_AUTH_TOKENSentry Organization Auth Token
SENTRY_ORGSentry organization slug

Linear Integration

VariableDescription
LINEAR_DISABLESet to true to disable Linear integration
LINEAR_CLIENT_IDOAuth app client ID (create at Linear > Settings > API > Applications)
LINEAR_CLIENT_SECRETOAuth app client secret (shown once on creation)
LINEAR_REDIRECT_URIOAuth callback URL (e.g., http://localhost:3013/api/trackers/linear/callback)
LINEAR_SIGNING_SECRETWebhook signing secret from Linear app settings
LINEAR_ALLOWED_STATESCSV of WorkflowState.type values that trigger task creation. Default: unstarted,started,completed,canceled (i.e. skip Backlog & Triage). Set to empty to lock down everything but the label override. See Linear integration → State gate.
LINEAR_SWARM_READY_LABELLabel name (case-insensitive) that bypasses the state gate. Default: swarm-ready.

Setup Steps

  1. Create an OAuth app at Linear > Settings > API > Applications
  2. Set Actor to "Application"
  3. Set Callback URL to your /api/trackers/linear/callback endpoint
  4. Enable "Agent session events" in webhook settings
  5. Set Webhook URL to your /api/trackers/linear/webhook endpoint
  6. Copy Client ID, Client Secret, and Webhook Signing Secret
  7. Start the server, then visit /api/trackers/linear/authorize to complete OAuth

With portless: set LINEAR_REDIRECT_URI=https://api.swarm.localhost:1355/api/trackers/linear/callback

Jira Integration

VariableDescription
JIRA_DISABLESet to true to disable Jira integration (or set JIRA_ENABLED=false)
JIRA_CLIENT_IDOAuth 2.0 (3LO) app client ID from Atlassian (developer.atlassian.com > My Apps > Settings). Its presence also gates whether the Jira integration initializes at all
JIRA_CLIENT_SECRETOAuth client secret paired with JIRA_CLIENT_ID
JIRA_REDIRECT_URIOAuth callback URL override — must match exactly what's registered in the Atlassian app. Defaults to <PUBLIC_MCP_BASE_URL or MCP_BASE_URL>/api/trackers/jira/callback
JIRA_WEBHOOK_TOKENHigh-entropy token embedded in the registered webhook URL path — Atlassian's 3LO webhooks aren't HMAC-signed, so this is the sole inbound auth mechanism. Generate with openssl rand -hex 32. Webhook register/receive endpoints return 503 until it's set

Setup Steps

  1. Create an OAuth 2.0 (3LO) app at developer.atlassian.com > My Apps
  2. Add scopes: read:jira-work, write:jira-work, manage:jira-webhook, offline_access, read:me
  3. Set the callback URL to your /api/trackers/jira/callback endpoint
  4. Copy the Client ID and Client Secret
  5. Generate a webhook token (openssl rand -hex 32) and set JIRA_WEBHOOK_TOKEN
  6. Start the server, then visit /api/trackers/jira/authorize to complete OAuth
  7. Register the dynamic webhook via POST /api/trackers/jira/webhook-register (admin, requires the swarm API key)

See the Jira Integration guide for the full walkthrough, webhook lifecycle, and known limitations.

Composio Integration

Environment variables for the Composio Tool Router integration, used by agent-swarm x composio and the swarm_x MCP tool to call third-party APIs through Composio.

VariableDescription
COMPOSIO_API_KEYProject API key, sent as x-api-key on Composio requests
COMPOSIO_ORG_API_KEYOptional organization key, sent as x-org-api-key when --org is passed
COMPOSIO_BASE_URLOptional API base URL override (default: https://backend.composio.dev/api/v3.1)

Portless (Local Development)

Portless replaces port-based URLs with friendly domain names for local development.

VariableWith PortlessDescription
MCP_BASE_URLhttps://api.swarm.localhost:1355API server URL
APP_URLhttps://ui.swarm.localhost:1355Dashboard URL

Install: bun add -g portless. Enable HTTPS: portless trust && portless proxy start --https.

UI Development Server

VariableDefaultDescription
VITE_PROXY_TARGEThttp://localhost:3013Overrides the API origin the ui/ Vite dev server (bun run dev, port 5274) proxies /api, /health, and /status requests to. Set when the API is running on a non-default port or host during local dashboard development.
VITE_API_URLunsetFixes a dedicated dashboard build to one API origin. Requires VITE_API_KEY. The connection switcher becomes a static label.
VITE_API_KEYunsetFixes the API credential for VITE_API_URL. Connection settings become read-only.
VITE_USER_IDunsetFixes the dashboard identity to an existing user. Requires VITE_API_URL and VITE_API_KEY.
VITE_DEMO_MODEfalseShows a diagonal live demo ribbon when set to true or 1.
VITE_PLAUSIBLE_ANALYTICSunsetBuild-time flag. Set to 1 to inject the Plausible analytics snippet into the dashboard's index.html during bun run build. Only our hosted dashboard sets it; self-hosted and local builds ship with no analytics script.
VITE_PLAUSIBLE_SCRIPT_IDhosted dashboard's idPlausible site script id (the pa-<id>.js part of the snippet) used when VITE_PLAUSIBLE_ANALYTICS is on. Set it on a second deployment, such as the public demo, so it reports to its own Plausible site.

Vite includes every VITE_* value in public browser assets. Use a restricted credential and a demo-safe API deployment for fixed public dashboards.

Cloud Personalization & Adaptive Home

Identity envs that brand the swarm and gate cloud-only UX. All are read on every GET /status call (cheap — env reads + one SQL aggregate, no upstream calls). See the Personalization & Status guide for the full story.

VariableDefaultDescription
SWARM_CLOUDfalseWhen true, marks the deployment as cloud-hosted. Surfaces Docs/Support/Billing items in the user-menu, suppresses the self-host marketing footer, and is sent as metadata.is_cloud (always present) on every telemetry event and sets context.deployment
SWARM_ORG_NAMESwarmSidebar header name. Also sent as metadata.organization_name and context.org_name on every telemetry event when set
SWARM_ORG_IDnoneStable org/tenant identifier exposed on /status as identity.org_id and sent as metadata.organization_id and context.org_id on every telemetry event when set. Set by the orchestrator on cloud deployments. Self-host installs that leave it unset get a random org_ ID instead
SWARM_ORG_LOGO_URLbundled /logo.pngSidebar logo URL (any HTTPS URL). Falls back to the bundled logo if it fails to load
SWARM_ORG_WEBSITEunsetCompany website as a bare hostname (example.com). When SWARM_ORG_LOGO_URL is empty, saving it in the dashboard fills the logo from the telemetry proxy (https://proxy.desplega.sh/v1/logo?domain=<host>). Sent with telemetry as org_website.
SWARM_BRAND_COLORnoneTints the org name in the sidebar header (any CSS color, e.g. #a855f7)
SWARM_MARKETING_URLnoneSelf-host marketing footer link target. Suppressed when SWARM_CLOUD=true or SWARM_HIDE_CLOUD_PROMO=true
SWARM_HIDE_CLOUD_PROMOfalseForce-hide the marketing footer regardless of SWARM_CLOUD
SWARM_VERIFY_TTL_MS3600000 (1h)How long a successful "Test connection" click keeps the harness milestone in verified state. In-memory; lost on API restart

Agent Filesystem (agent-fs)

VariableDescription
AGENT_FS_API_URLAgent-fs API URL. When unset, the server uses local-fs; when set as env or global swarm config, it enables the persistent shared filesystem provider
AGENT_FS_LIVE_URLAgent-fs live host used to build shareable file links (<AGENT_FS_LIVE_URL>/file/~/<org_id>/<drive_id>/<file_path>). Falls back to the public https://live.agent-fs.dev host when unset; self-hosted agent-fs operators should override it
AGENT_FS_PUBLIC_URLAgent-fs URL that browsers use for Comb. Defaults to AGENT_FS_API_URL; set it when the API reaches agent-fs on an internal hostname (e.g. http://agent-fs:7433 in Docker Compose, where the browser needs http://localhost:7433). Must be an http(s) URL with no query string or fragment
COMB_ENABLEDTurns on Comb, the agent-fs review space in the dashboard (default false). No effect until AGENT_FS_API_URL is set. Saving it in swarm config reloads the API; the dashboard picks it up within one status poll (30 seconds at most)
API_AGENT_FS_API_KEYAPI-owned bootstrap/service API key for agent-fs. Used only by the API server to seed the swarm org/drive and invite/register agents
AGENT_FS_API_KEYPer-agent API key for agent-fs CLI/MCP access. Generated by the API server and stored as an agent-scoped secret; workers should not share the API bootstrap key
AGENT_FS_SHARED_ORG_IDShared org ID for the swarm's agent-fs organization. Auto-created by the lead on first boot
AGENT_FS_DEFAULT_ORG_IDDefault agent-fs org ID used to auto-resolve agent-fs attachment rows on store-progress when orgId is missing. Scope precedence: agent > global
AGENT_FS_DEFAULT_DRIVE_IDDefault agent-fs drive ID used to auto-resolve agent-fs attachment rows on store-progress when driveId is missing. Scope precedence: agent > global

The no-config default is local-fs; set AGENT_FS_API_URL globally to keep shared agent-fs behavior. The API boot seeder registers a service identity with agent-fs on first boot, stores API_AGENT_FS_API_KEY as an encrypted global swarm config secret, creates the shared org/drive, and persists AGENT_FS_DEFAULT_*. Each runner then asks the API to create its own encrypted agent-scoped AGENT_FS_API_KEY. The two AGENT_FS_DEFAULT_* keys let the server fill in attachment IDs server-side so the renderer can emit full live-host URLs — per-row IDs always win, and missing config + missing row IDs still falls back to the agent-fs:<path> form.

Pages

Environment variables for the hosted Pages feature (DB-backed, agent-authored web pages). See MCP Tools Reference for the Pages tools.

VariableDescription
PAGE_SESSION_SECRETHMAC-SHA256 secret for signing the page_session cookie. Reserved-like: not read from swarm_config, and never falls back to the swarm API key (that key's documented default is public). When unset, resolved via PAGE_SESSION_SECRET_FILE, then <dirname(DATABASE_PATH)>/.page-session-secret on disk, then auto-generated and persisted there on first use — mirrors SECRETS_ENCRYPTION_KEY's resolution order (see Encryption Key).
PAGE_SESSION_SECRET_FILEAlternative to PAGE_SESSION_SECRET: absolute path to a file whose contents are the secret. Useful with Docker secrets or k8s Secret volume mounts.

x402 Payments

Environment variables for the x402 payment module, enabling agents to make USDC micropayments on x402-gated APIs.

Common

VariableDefaultDescription
X402_SIGNER_TYPEAuto-detectedSigner backend: "openfort" or "viem". Auto-detects based on which credentials are set
X402_MAX_AUTO_APPROVE1.00Maximum USD amount to auto-approve per request
X402_DAILY_LIMIT10.00Daily spending limit in USD
X402_NETWORKeip155:84532CAIP-2 network ID. eip155:84532 = Base Sepolia (testnet), eip155:8453 = Base mainnet

Openfort Signer

VariableRequiredDescription
OPENFORT_API_KEYYesOpenfort API key (sk_test_ or sk_live_ prefixed)
OPENFORT_WALLET_SECRETYesP-256 ECDSA key for wallet authentication (base64 encoded)
OPENFORT_WALLET_ADDRESSNoReuse existing wallet address instead of creating a new one

Viem Signer

VariableRequiredDescription
EVM_PRIVATE_KEYYesWallet private key (0x-prefixed hex). Use a burner wallet with minimal funds

Multi-Credential Support

To distribute load across multiple Claude subscriptions, provide multiple credentials as comma-separated values:

# Multiple OAuth tokens — one is randomly selected per session
CLAUDE_CODE_OAUTH_TOKEN=token1,token2,token3

# Also works with API keys
ANTHROPIC_API_KEY=sk-key1,sk-key2

When a session is spawned, the runner splits the credential value by commas and randomly selects one. Each session gets a single credential, distributing load across subscriptions. A log line indicates which credential index was selected (never the credential itself). Single values (no commas) work unchanged — fully backward compatible.

On the pi and opencode harnesses, OPENROUTER_API_KEY, NAN_API_KEY (or HELMCODE_API_NAN) and HELMCODE_API_KEY pool the same way: each task gets one key, picked by the model's provider prefix. On opencode the picked key is handed to the provider config, so an OPENROUTER_API_KEY pool works there too.

NaN and Helmcode cap concurrency per key (NaN: 60 RPM, 7 concurrent requests in total on the base plan, 10 on premium, 5 per model on qwen3.6, gemma4 and the mimo models; Helmcode: 100 RPM, 2M tokens per minute, 5 concurrent requests per model, 10 on the GLM family, DeepSeek V4 Flash and Qwen 3.8 Flash). One key shared across a fleet hits these caps fast, so use a pool.

Telemetry

VariableDefaultDescription
ANONYMIZED_TELEMETRYtrue (enabled)Set to false to disable anonymized telemetry. Events carry an org ID, the org name and the admin's email domain, and a pseudonymous user reference (never an email address); see Identity context
DESPLEGA_TELEMETRY_ENVproductionExplicit telemetry environment tag. Set to development or test for intentional non-production telemetry.
PUBLIC_RANKING_ENABLEDfalseOpt-in to the public install ranking. Set by Get ranked in Settings → Configuration → Branding rather than by hand. While telemetry is on and SWARM_ORG_NAME and SWARM_ORG_WEBSITE are set, workers send ranking.usage token counts after each session; see Public ranking
INSTALL_METHODunsetWritten automatically by the onboard wizard (onboard_interactive / onboard_noninteractive); not meant to be hand-set. Unset installs are attributed as manual (or e2b inside an E2B sandbox).
INSTALL_PRESETunsetWritten automatically by the onboard wizard with the chosen preset (e.g. solo), when applicable.

OpenTelemetry

OpenTelemetry traces and OTLP metrics are disabled unless OTEL_EXPORTER_OTLP_ENDPOINT is set. See Observability with OpenTelemetry for SigNoz setup, emitted spans, metric counters, and query examples.

VariableDefaultDescription
OTEL_EXPORTER_OTLP_ENDPOINTnoneBase OTLP HTTP endpoint shared by traces and metrics. For SigNoz Cloud, use your region's ingest URL (e.g. https://ingest.eu2.signoz.cloud) and let the SDK append /v1/traces and /v1/metrics.
OTEL_EXPORTER_OTLP_HEADERSnoneOTLP exporter headers, for example signoz-ingestion-key=<key>. Treated as sensitive by the secret scrubber
OTEL_EXPORTER_OTLP_PROTOCOLSDK defaultExport protocol. Use http/protobuf for SigNoz Cloud
OTEL_SERVICE_NAMEagent-swarm-api or agent-swarm-worker outside local composeOpenTelemetry service.name. Set to agent-swarm everywhere to keep one service and split by agentswarm.service.role
OTEL_RESOURCE_ATTRIBUTESderived from NODE_ENVComma-separated resource attributes such as deployment.environment=local,env=local,service.namespace=agent-swarm
OTEL_TRACE_POLLOFFWhen set to 1/true, emits trace spans for the worker poll loop (worker.poll) and the /api/poll HTTP endpoint. Skip by default to reduce span volume.
OTEL_EXPORT_API_LOGSOFFWhen set to 1/true on the API, also exports its console output as OTLP log records to the same endpoint as traces, scrubbed of secrets. Read at startup.

Business-Use Instrumentation

Optional integration with @desplega.ai/business-use for tracking system invariants across the distributed API + worker architecture.

VariableDescription
BUSINESS_USE_API_KEYAPI key from uvx business-use-core@latest init
BUSINESS_USE_URLBU backend URL (default: http://localhost:13370)

SDK enters no-op mode if the API key is missing — safe to omit in environments without a BU backend.

Secrets Encryption

swarm_config rows with isSecret=1 are encrypted at rest using AES-256-GCM (v1.67.0+). The server resolves the encryption key on boot in this order:

  1. SECRETS_ENCRYPTION_KEY env var (base64-encoded 32 bytes)
  2. SECRETS_ENCRYPTION_KEY_FILE pointing at a file containing the base64 key
  3. <data-dir>/.encryption-key file on disk
  4. Auto-generated on first boot (only when the DB has no existing encrypted rows)

Generate a key with:

openssl rand -base64 32 > ./encryption_key
chmod 600 ./encryption_key

Back up and preserve the encryption key alongside your SQLite database. Losing the key means losing all encrypted secrets (API tokens, OAuth creds, etc.) with no recovery path. Do not switch between key sources unless the underlying base64 value is identical.

Reserved keys: API_KEY and SECRETS_ENCRYPTION_KEY are rejected by the swarm_config API (case-insensitive) and must remain environment-only.

Upgrading from plaintext: Legacy secrets are auto-migrated on first boot. If SECRETS_ENCRYPTION_KEY was not set beforehand, a one-time plaintext backup is created at <db-path>.backup.secrets-YYYY-MM-DD.env. Delete this file after verifying your key is backed up.

Priority

When both CLI flags and environment variables are set:

  • CLI flags take precedence over environment variables
  • Inline text (SYSTEM_PROMPT) takes precedence over file (SYSTEM_PROMPT_FILE)

Credentialed CORS wildcard patterns

CORS_ALLOWED_ORIGINS accepts exact origins and patterns such as https://*.agent-swarm.dev. A single leading *. matches one or more complete subdomain labels, including app.agent-swarm.dev and a.b.agent-swarm.dev, but never the apex agent-swarm.dev; list the apex separately if needed. Wildcard host comparison is case-insensitive. Schemes and explicit ports must match exactly; https://*.agent-swarm.dev:8443 only allows that port. Exact entries retain their existing case-sensitive comparison. Patterns reject evil-agent-swarm.dev, app.agent-swarm.dev.evil.com, and an http origin against an https pattern. Bare *, https://*, and wildcards in other positions are ignored, never treated as allow-all.

Breaking change: secure CORS defaults

Unset and blank values now use this built-in allowlist:

CORS_ALLOWED_ORIGINS=https://*.agent-swarm.dev,https://*.agent-swarm.cloud,http://localhost:5274,http://127.0.0.1:5274,http://[::1]:5274,https://ui.swarm.localhost:1355

A custom value replaces these defaults, including localhost. For an affected self-hosted SPA, set CORS_ALLOWED_ORIGINS=https://dashboard.example.com on the API (substitute its actual origin). Include every trusted origin you need, comma-separated. The hosted patterns exclude apex domains.

For arbitrary-origin bearer-token clients, set CORS_ALLOW_ANY_ORIGIN=true and use credentials: "omit" in browser requests. This permits non-credentialed CORS only. Cookie-authenticated responses, including page JSON and /@swarm/api/*, always require an allowlisted origin for Access-Control-Allow-Credentials; the flag cannot override that check. Only CORS_ALLOWED_ORIGINS takes effect after Configuration reload. The bypass is deployment-only: set it in the API environment and restart. Config writes through HTTP, MCP, or database helpers reject the reserved key; legacy stored values are ignored at startup and reload and can be deleted. The API warns once per process at startup or first use when the deployment opt-out is enabled. Denied-origin logs name the origin and CORS_ALLOWED_ORIGINS so a missing SPA origin is diagnosable. CORS controls browser response access; it does not replace authentication or CSRF protection.

On this page