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
| Variable | Default | Description |
|---|---|---|
PORT | 3013 | Port 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_ENABLED | false | Gates REST requests authenticated with aswt_ user tokens against RBAC role grants. Operator API-key requests and agent-authenticated calls are unaffected. Keep it off until you have seeded roles and validated the permission model for your deployment. |
SCRIPTS_ONLY_MCP | false (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_URL | https://api.example-swarm.dev | Internal/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_URL | Falls back to MCP_BASE_URL | Public, 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_HOSTS | false | When 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. |
OAUTH_KEEPALIVE_DISABLE | false | Disables the background job that proactively refreshes Linear/Jira OAuth refresh tokens every 12h to prevent silent expiry. |
SWARM_URL | localhost | Base domain for service discovery |
APP_URL | — | Dashboard URL for Slack message links. DASHBOARD_URL is a deprecated alias of APP_URL. |
SWARM_DASHBOARD_URL | Falls back to APP_URL | Optional 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_ID | false (unset/off) | Set to true only in single-tenant/shared-operator-key deployments where every POST /api/tasks caller is already trusted. When enabled, the server may honor a body-supplied requestedByUserId (validated against a real user row) if no authenticated/owned-task identity is available. Leave it off in multi-tenant or upstream-safe deployments to preserve the default anti-spoofing behavior. |
ENV | — | Environment mode (development adds prefix to Slack agent names) |
DATABASE_PATH | ./agent-swarm-db.sqlite | SQLite database file path |
ASSET_KEY_AUDIT_DISABLE_STARTUP_HARD_FAIL | false | Temporary 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_RUNTIME_DIR | — | Directory holding the pre-built script-runtime bundles (eval-harness.bundle.js, stdlib.bundle.js, swarm-sdk.bundle.js, zod.bundle.js). 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 | — | Fallback directory for .sql migration files when import.meta.dir can't be read (compiled-binary mode, since the harness subprocess can't scan the binary's virtual filesystem). Set automatically in the Docker image (/app/migrations). Only needed for non-Docker/compiled-binary 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_KEY | Auto-generated on first boot | Master 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_MS | 10000 | Polling interval for scheduled tasks (ms) |
SCRIPT_RUN_CONCURRENCY_CAP | 10 | Maximum 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_DISABLE | false | When 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_DEBUG | false | When 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_ITERATIONS | 100 | Max 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_RUN | 500 | Max 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. |
EMBEDDING_MODEL | text-embedding-3-small | Embedding model slug sent to the provider. |
EMBEDDING_DIMENSIONS | 512 | Vector dimensions requested from the model. Must match what the model supports. |
MEMORY_RECENCY_HALF_LIFE_DAYS | 14 | Optional 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_SEARCH | 0 | Set to 1 to enable hybrid memory retrieval that blends vector similarity with a full-text pass before reranking. Leave unset or 0 for vector-only search. |
MEMORY_GRAPH_EXPANSION | 0 | Set to 1 to expand 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. Default off — results are byte-identical to pre-expansion behavior. |
MEMORY_MIN_SIMILARITY | 0.1 | Minimum raw cosine similarity required before a memory candidate survives reranking. Filters low-relevance noise before recency/access boosts are applied. |
MEMORY_ACCESS_BOOST_MAX | 1.5 | Maximum access boost multiplier for reranking |
MEMORY_ACCESS_RECENCY_HOURS | 48 | Hours within which access counts for full boost |
MEMORY_CANDIDATE_MULTIPLIER | 3 | Candidate set size relative to requested limit |
MEMORY_DEMOTION_FLOOR | 1.0 | Floor 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 |
MEMORY_RATERS | — (unset/empty → no-op rater only) | Comma-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/empty is byte-identical to pre-rater behavior. |
MEMORY_RATER_MODEL | Per credential kind, e.g. google/gemini-3-flash-preview (OpenRouter), anthropic/claude-haiku-4-5, openai/gpt-5.4-mini | 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. |
MEMORY_RATER_WEIGHTS | 1.0 per rater | Optional 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]. |
CAPABILITIES | All enabled | Comma-separated feature flags |
SKILL_FILES_MAX_COUNT | 100 | Max number of files allowed in a single skill file-set upsert. |
SKILL_FILES_MAX_FILE_BYTES | 512000 (500 KB) | Max size in bytes for a single file within a skill file-set upsert. |
SKILL_FILES_MAX_TOTAL_BYTES | 10485760 (10 MB) | Max combined size in bytes for all files in a single skill file-set upsert. |
BUDGET_ADMISSION_DISABLED | false | Operator 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. |
HEARTBEAT_INTERVAL_MS | 90000 | Heartbeat sweep interval (ms) |
HEARTBEAT_CHECKLIST_DISABLE | false | Disables the periodic HEARTBEAT.md checklist polling loop (separate from the infrastructure sweep). |
HEARTBEAT_CHECKLIST_INTERVAL_MS | 1800000 | Interval (ms) between HEARTBEAT.md checklist checks (default 30 min). |
HEARTBEAT_DISABLE | false | Set to true to disable the heartbeat module |
HEARTBEAT_STALL_THRESHOLD_MIN | 30 | Minutes before an in-progress task is considered stalled |
HEARTBEAT_STALL_NO_SESSION_MIN | 5 | Minutes before an in-progress task with no active worker session is considered stalled |
HEARTBEAT_STALL_STALE_HB_MIN | 15 | Minutes before an in-progress task with a stale worker heartbeat is considered stalled |
HEARTBEAT_STALE_CLEANUP_MIN | 30 | Minutes before stale resources (sessions, reviewing tasks) are cleaned up |
HEARTBEAT_MAX_AUTO_ASSIGN | 5 | Max pool tasks to auto-assign per sweep |
HEARTBEAT_MAX_RESUME_GENERATIONS | 3 | Max crash-recovery resume generations for a task before it is failed for Lead triage instead of resumed again |
HEARTBEAT_PIN_CRASH_RESUME | true (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_RESUME | true (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_MIN | 10 | Grace 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 |
WORKER_LIVENESS_WINDOW_SECONDS | 30 | Seconds 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):
SECRETS_ENCRYPTION_KEYenv varSECRETS_ENCRYPTION_KEY_FILEenv var (path to a file containing the key)<data-dir>/.encryption-keyon the API's data volume- 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
| Variable | Required | Default | Description |
|---|---|---|---|
HARNESS_PROVIDER | No | claude | AI provider: claude (Claude Code), codex, opencode, pi (pi-mono), devin (Devin), or claude-managed (Anthropic Managed Agents). See Harness Configuration |
Credentials
Which credentials you need depends on your selected harness provider:
Claude Code (HARNESS_PROVIDER=claude, default):
| Variable | Required | Default | Description |
|---|---|---|---|
CLAUDE_CODE_OAUTH_TOKEN | Yes* | — | OAuth token for Claude CLI. Supports comma-separated values for multi-credential load balancing |
ANTHROPIC_API_KEY | Alt* | — | Alternative to CLAUDE_CODE_OAUTH_TOKEN. Also supports comma-separated values |
SWARM_USE_CLAUDE_BRIDGE | No | false | Reloadable 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_BINARY | No | claude | Low-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_1H | No | 1 | Enables 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):
| Variable | Required | Default | Description |
|---|---|---|---|
ANTHROPIC_API_KEY | One of* | — | Anthropic API key for Claude models via pi-mono |
OPENROUTER_API_KEY | One of* | — | OpenRouter API key for multi-provider model access |
BEDROCK_AUTH_MODE | No | inferred from MODEL_OVERRIDE | Optional Bedrock mode override. Set sdk to force AWS SDK credential probing / live model enumeration; bearer is reserved for future support |
AWS_REGION | In Bedrock SDK mode | — | Required region for Bedrock credential probing and account-accurate model enumeration when BEDROCK_AUTH_MODE=sdk or MODEL_OVERRIDE=amazon-bedrock/* |
* 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):
| Variable | Required | Default | Description |
|---|---|---|---|
OPENAI_API_KEY | Optional | — | Standard OpenAI API key for Codex |
API_KEY | Yes | — | Swarm API key used to fetch codex_oauth from the config store |
MCP_BASE_URL | Yes | http://host.docker.internal:3013 | Swarm API URL reachable by the worker |
CODEX_SKILLS_DIR | No | ~/.codex/skills | Directory used by the Codex inline slash-command resolver to find <name>/SKILL.md (mirrors OPENCODE_SKILLS_DIR) |
CODEX_PATH_OVERRIDE | No | Set automatically in the Docker image (/usr/bin/codex) | Absolute path to the Codex CLI wrapper/binary, passed to the SDK's codexPathOverride. Needed because the bundled @openai/codex-sdk can't require.resolve from a compiled Bun binary. Only needed for non-Docker/custom installs |
AGENT_SWARM_CODEX_RUNNER_ARGV | No | Inferred 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_CERTS | No | — | 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.
opencode (HARNESS_PROVIDER=opencode):
| Variable | Required | Default | Description |
|---|---|---|---|
OPENROUTER_API_KEY | One of* | — | OpenRouter API key (primary — unlocks 100+ models at openrouter.ai) |
ANTHROPIC_API_KEY | One of* | — | Anthropic API key for Claude models |
OPENAI_API_KEY | One of* | — | OpenAI API key for GPT models |
OPENCODE_BINARY | No | opencode | Path to the opencode CLI binary (if not in $PATH) |
OPENCODE_SKILLS_DIR | No | ~/.opencode/skills | Directory used by the OpenCode inline resolver for /<skill> prompts |
OPENCODE_SERVER_TIMEOUT_MS | No | 30000 | Timeout (ms) for the opencode SDK's server-start call; override when cold-start on slow disks (e.g. E2B) exceeds the SDK's own 5s default and spawn fails with "Timeout waiting for server to start" |
OPENCODE_SWARM_PLUGIN_PATH | No | Set automatically in the Docker image | Explicit override for the absolute path to the agent-swarm opencode plugin entrypoint; falls back to the Docker path if present, else a dev-relative path from source |
CONTEXT_MODE_OPENCODE_PLUGIN_PATH | No | Auto-discovered under the global npm root | Override for the absolute path to context-mode's built opencode plugin entry, used when the default global npm-root search can't find it — otherwise context-mode is silently skipped for that session |
MODEL_OVERRIDE | No | openrouter/qwen/qwen3-coder-flash | Model 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, or ~/.local/share/opencode/auth.json is required.
Devin (HARNESS_PROVIDER=devin):
| Variable | Required | Default | Description |
|---|---|---|---|
DEVIN_API_KEY | Yes | — | Devin API key (prefix: cog_*) |
DEVIN_ORG_ID | Yes | — | Devin organization ID (prefix: org-*) |
DEVIN_POLL_INTERVAL_MS | No | 15000 | Polling interval for Devin session events (ms) |
DEVIN_ACU_COST_USD | No | 2.25 | USD per ACU for cost tracking |
DEVIN_API_BASE_URL | No | https://api.devin.ai | Devin API base URL (override for testing) |
DEVIN_MAX_ACU_LIMIT | No | — | Per-session ACU cap (sent to Devin API + UI budget bar) |
MAX_SKILL_CHARS | No | 100000 | Max chars for inlined SKILL.md content |
HAS_MCP | Conditional | false | Declares 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_DIR | No | <project-root>/plugin/pi-skills | Directory 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):
| Variable | Required | Default | Description |
|---|---|---|---|
ANTHROPIC_API_KEY | Yes | — | Anthropic API key (used by both the setup CLI and the runtime adapter) |
MANAGED_AGENT_ID | Yes | — | Anthropic Agent ID. Written by claude-managed-setup |
MANAGED_ENVIRONMENT_ID | Yes | — | Anthropic Environment ID. Written by claude-managed-setup |
MCP_BASE_URL | Yes | — | Must be HTTPS-public so Anthropic's sandbox can reach /mcp. Worker fails fast at boot otherwise |
MANAGED_AGENT_MODEL | No | claude-sonnet-5 | Default model on sessions.create; per-task task.model overrides |
MANAGED_GITHUB_VAULT_ID | No | — | Anthropic vault ID holding a GitHub PAT, for repo-bound tasks (recommended for prod) |
MANAGED_GITHUB_TOKEN | No | — | Literal GitHub PAT injected as authorization_token on github_repository resources (dev-only fallback) |
MANAGED_MCP_VAULT_ID | No | — | 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_HOUR | No | 0.08 | USD-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
| Variable | Required | Default | Description |
|---|---|---|---|
API_KEY | Yes | — | API key for MCP server |
AGENT_ID | No | Auto-generated | Agent UUID. Keep stable for task resume |
AGENT_ROLE | No | worker | Role: worker or lead |
AGENT_NAME | No | Auto-generated | Display name for the agent |
MCP_BASE_URL | No | http://host.docker.internal:3013 | MCP server URL |
SESSION_ID | No | Auto-generated | Log folder name |
LOG_DIR | No | /logs | Base directory for session .jsonl log files (<LOG_DIR>/<sessionId>/<timestamp>-<taskId8>.jsonl) |
YOLO | No | false | Continue on errors |
SYSTEM_PROMPT | No | — | Custom system prompt text |
SYSTEM_PROMPT_FILE | No | — | Path to system prompt file |
CONTEXT_MODE_DISABLED | No | false | Set to true to disable the default context-mode MCP wiring for local Claude Code, Codex, and opencode workers. Leaves the worker running but hides/skips the ctx_* tools. |
SCRIPTS_ONLY_MCP | No | false | Experimental 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_EVERY | No | 3 | How often (in turns) the context-mode MCP plugin surfaces its external-MCP guidance nudge, across Claude Code, Codex, and opencode alike |
CONTEXT_PREAMBLE_MAX_TOKENS | No | 2000 | Token budget for the universal follow-up context preamble prepended to a child task's prompt; applies across all harness providers |
CONTEXT_PREAMBLE_RESUME_MAX_TOKENS | No | 4000 | Token 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_STRICT | No | false | Exit 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_TIMEOUT | No | 30000 | Grace period (ms) before pausing tasks |
MAX_CONCURRENT_TASKS | No | 1 | Maximum parallel tasks per worker |
SWARM_URL | No | localhost | Base domain for service URLs |
LEAD_PORT | No | 3020 | Host 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_PORT | No | 3021 | Host port for worker-1 service. Example — see LEAD_PORT. |
WORKER2_PORT | No | 3022 | Host port for worker-2 service. Example — see LEAD_PORT. |
PM2_HOME | No | /workspace/.pm2 | PM2 state directory |
TEMPLATE_ID | No | — | Template for initial profile on first boot (e.g., official/coder) |
TEMPLATE_REGISTRY_URL | No | https://templates.agent-swarm.dev | URL of the templates registry |
MODEL_TIER_MAP | No | — | 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_ENABLED | No | false | Set 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_DIR | No | /tmp/postgres-data | Data directory for the optional local Postgres cluster |
LOCAL_POSTGRES_PORT | No | 5433 | Port for the optional local Postgres cluster |
LOCAL_POSTGRES_USER | No | postgres | Superuser name for the optional local Postgres cluster |
LOCAL_POSTGRES_PASSWORD | No | postgres | Superuser password for the optional local Postgres cluster |
LOCAL_POSTGRES_DB | No | app | Database created in the optional local Postgres cluster |
Git Configuration
| Variable | Default | Description |
|---|---|---|
GITHUB_TOKEN | — | GitHub token for git operations |
GITHUB_EMAIL | worker-agent@desplega.ai | Git commit email |
GITHUB_NAME | Worker Agent | Git commit name |
Slack Integration
| Variable | Description |
|---|---|
SLACK_BOT_TOKEN | Bot User OAuth Token (xoxb-...) |
SLACK_APP_TOKEN | App-Level Token for Socket Mode (xapp-...) |
SLACK_SIGNING_SECRET | Signing Secret (optional for Socket Mode) |
SLACK_DISABLE | Set to true to disable Slack |
SLACK_ALLOWED_EMAIL_DOMAINS | Comma-separated email domains |
SLACK_ALLOWED_USER_IDS | Comma-separated user IDs to always allow |
ADDITIVE_SLACK | Set to true to enable non-mention thread message buffering and batching |
ADDITIVE_SLACK_BUFFER_MS | Debounce window for thread buffer in ms (default: 10000) |
SLACK_THREAD_FOLLOWUP_REQUIRE_MENTION | Set to true to require @mention for thread follow-up routing (default: false) |
LEAD_MONITOR_CHANNELS | Set 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_IDS | Comma-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_ALERTS_CHANNEL | Slack channel ID/name that the OAuth keepalive (Linear/Jira token-refresh failures) and Jira webhook keepalive post failure alerts to. Unset means alerts are logged only, not sent |
GitHub Integration
| Variable | Description |
|---|---|
GITHUB_WEBHOOK_SECRET | Webhook secret for GitHub App |
GITHUB_BOT_NAME | Bot name for @mentions (default: agent-swarm-bot) |
GITHUB_BOT_ALIASES | Comma-separated additional @mention aliases (e.g. heysidekick,sidekick) |
GITHUB_EVENT_LABELS | Comma-separated labels that trigger agent action on PR/issue label events (default: swarm-review) |
GITHUB_APP_ID | GitHub App ID (for bot reactions) |
GITHUB_APP_PRIVATE_KEY | GitHub App private key (base64-encoded) |
GITHUB_DISABLE | Set to true to disable GitHub |
GitLab Integration
| Variable | Description |
|---|---|
GITLAB_TOKEN | GitLab PAT or Group Access Token for API calls |
GITLAB_URL | GitLab instance URL (default: https://gitlab.com) |
GITLAB_WEBHOOK_SECRET | Shared secret for webhook verification |
GITLAB_BOT_NAME | Bot username for @mention detection (default: agent-swarm-bot) |
GITLAB_EMAIL | Git commit email for GitLab repos |
GITLAB_NAME | Git commit name for GitLab repos |
GITLAB_DISABLE | Set to true to disable GitLab integration |
AgentMail Integration
| Variable | Description |
|---|---|
AGENTMAIL_DISABLE | Set to true to skip AgentMail integration |
AGENTMAIL_WEBHOOK_SECRET | Svix signing secret for webhook verification |
AGENTMAIL_INBOX_DOMAIN_FILTER | Comma-separated domains to allow for incoming inbox webhooks (e.g., yourdomain.com,example.com). Unmatched inbox domains are silently dropped |
AGENTMAIL_SENDER_DOMAIN_FILTER | Comma-separated sender domains to allow (e.g., gmail.com,company.com). Unmatched sender domains are silently dropped |
ADDITIVE_AGENTMAIL | Set to true to enable non-mention thread message buffering and batching for AgentMail, mirroring ADDITIVE_SLACK |
ADDITIVE_AGENTMAIL_BUFFER_MS | Debounce window (ms) for buffering rapid AgentMail follow-up messages into a single task when ADDITIVE_AGENTMAIL=true is set (default: 10000) |
Kapso / WhatsApp Integration
| Variable | Description |
|---|---|
KAPSO_API_KEY | Kapso API key used for outbound sends and optional webhook registration (X-API-Key) |
KAPSO_PHONE_NUMBER_ID | Default WhatsApp Business phone-number ID the swarm sends from |
KAPSO_WEBHOOK_HMAC_SECRET | Shared secret used to verify Kapso's X-Webhook-Signature header on inbound webhooks |
KAPSO_API_BASE_URL | Optional Kapso API base URL override (default: https://api.kapso.ai) |
Setup Steps
- Set the values above in your
.envor integrations dashboard. - Have the lead call
register-kapso-numberto point the number at/api/integrations/kapso/webhookand store the routing mapping. - Use
send-whatsapp-message/reply-whatsapp-messagefor the common text path, or thekapso-whatsappskill for templates, media, and reactions.
Sentry Integration
| Variable | Description |
|---|---|
SENTRY_AUTH_TOKEN | Sentry Organization Auth Token |
SENTRY_ORG | Sentry organization slug |
Linear Integration
| Variable | Description |
|---|---|
LINEAR_DISABLE | Set to true to disable Linear integration |
LINEAR_CLIENT_ID | OAuth app client ID (create at Linear > Settings > API > Applications) |
LINEAR_CLIENT_SECRET | OAuth app client secret (shown once on creation) |
LINEAR_REDIRECT_URI | OAuth callback URL (e.g., http://localhost:3013/api/trackers/linear/callback) |
LINEAR_SIGNING_SECRET | Webhook signing secret from Linear app settings |
LINEAR_ALLOWED_STATES | CSV 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_LABEL | Label name (case-insensitive) that bypasses the state gate. Default: swarm-ready. |
Setup Steps
- Create an OAuth app at Linear > Settings > API > Applications
- Set Actor to "Application"
- Set Callback URL to your
/api/trackers/linear/callbackendpoint - Enable "Agent session events" in webhook settings
- Set Webhook URL to your
/api/trackers/linear/webhookendpoint - Copy Client ID, Client Secret, and Webhook Signing Secret
- Start the server, then visit
/api/trackers/linear/authorizeto complete OAuth
With portless: set LINEAR_REDIRECT_URI=https://api.swarm.localhost:1355/api/trackers/linear/callback
Jira Integration
| Variable | Description |
|---|---|
JIRA_DISABLE | Set to true to disable Jira integration (or set JIRA_ENABLED=false) |
JIRA_CLIENT_ID | OAuth 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_SECRET | OAuth client secret paired with JIRA_CLIENT_ID |
JIRA_REDIRECT_URI | OAuth 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_TOKEN | High-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
- Create an OAuth 2.0 (3LO) app at developer.atlassian.com > My Apps
- Add scopes:
read:jira-work,write:jira-work,manage:jira-webhook,offline_access,read:me - Set the callback URL to your
/api/trackers/jira/callbackendpoint - Copy the Client ID and Client Secret
- Generate a webhook token (
openssl rand -hex 32) and setJIRA_WEBHOOK_TOKEN - Start the server, then visit
/api/trackers/jira/authorizeto complete OAuth - 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.
| Variable | Description |
|---|---|
COMPOSIO_API_KEY | Project API key, sent as x-api-key on Composio requests |
COMPOSIO_ORG_API_KEY | Optional organization key, sent as x-org-api-key when --org is passed |
COMPOSIO_BASE_URL | Optional 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.
| Variable | With Portless | Description |
|---|---|---|
MCP_BASE_URL | https://api.swarm.localhost:1355 | API server URL |
APP_URL | https://ui.swarm.localhost:1355 | Dashboard URL |
Install: bun add -g portless. Enable HTTPS: portless trust && portless proxy start --https.
UI Development Server
| Variable | Default | Description |
|---|---|---|
VITE_PROXY_TARGET | http://localhost:3013 | Overrides the API origin the ui/ Vite dev server (pnpm dev, port 5274) proxies /api, /health, and /status requests to. Set when the API is running on a non-default port/host during local dashboard development. |
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.
| Variable | Default | Description |
|---|---|---|
SWARM_CLOUD | false | When 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 anonymized telemetry event |
SWARM_ORG_NAME | Swarm | Sidebar header name. Also sent as metadata.organization_name on every anonymized telemetry event when set |
SWARM_ORG_ID | none | Stable org/tenant identifier exposed on /status as identity.org_id and sent as metadata.organization_id on every anonymized telemetry event when set. Set by the orchestrator on cloud deployments |
SWARM_ORG_LOGO_URL | bundled /logo.png | Sidebar logo URL (any HTTPS URL). Falls back to the bundled logo if it fails to load |
SWARM_BRAND_COLOR | none | Tints the org name in the sidebar header (any CSS color, e.g. #a855f7) |
SWARM_MARKETING_URL | none | Self-host marketing footer link target. Suppressed when SWARM_CLOUD=true or SWARM_HIDE_CLOUD_PROMO=true |
SWARM_HIDE_CLOUD_PROMO | false | Force-hide the marketing footer regardless of SWARM_CLOUD |
SWARM_VERIFY_TTL_MS | 3600000 (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)
| Variable | Description |
|---|---|
AGENT_FS_API_URL | Agent-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_URL | Agent-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 |
API_AGENT_FS_API_KEY | API-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_KEY | Per-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_ID | Shared org ID for the swarm's agent-fs organization. Auto-created by the lead on first boot |
AGENT_FS_DEFAULT_ORG_ID | Default 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_ID | Default 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.
Archil Disk Mounts
Environment variables for optional Archil FUSE disk mounts, giving worker containers persistent shared and per-agent personal disks. Skipped entirely when ARCHIL_MOUNT_TOKEN is unset (local dev / non-Archil environments).
| Variable | Description |
|---|---|
ARCHIL_MOUNT_TOKEN | Auth token for the Archil CLI. Gates the entire feature — unset disables disk mounting |
ARCHIL_REGION | Archil region for the mount (e.g. aws-eu-west-1) |
ARCHIL_SHARED_DISK_NAME | Shared disk identifier, mounted at /workspace/shared |
ARCHIL_PERSONAL_DISK_NAME | Per-agent personal disk identifier, mounted at /workspace/personal |
Pages
Environment variables for the hosted Pages feature (DB-backed, agent-authored web pages). See MCP Tools Reference for the Pages tools.
| Variable | Description |
|---|---|
PAGE_SESSION_SECRET | Optional HMAC-SHA256 secret for signing the page_session cookie. Falls back to the swarm API key (getApiKey()) when unset; boot-time signing/verification refuses to run if both are unset |
x402 Payments
Environment variables for the x402 payment module, enabling agents to make USDC micropayments on x402-gated APIs.
Common
| Variable | Default | Description |
|---|---|---|
X402_SIGNER_TYPE | Auto-detected | Signer backend: "openfort" or "viem". Auto-detects based on which credentials are set |
X402_MAX_AUTO_APPROVE | 1.00 | Maximum USD amount to auto-approve per request |
X402_DAILY_LIMIT | 10.00 | Daily spending limit in USD |
X402_NETWORK | eip155:84532 | CAIP-2 network ID. eip155:84532 = Base Sepolia (testnet), eip155:8453 = Base mainnet |
Openfort Signer
| Variable | Required | Description |
|---|---|---|
OPENFORT_API_KEY | Yes | Openfort API key (sk_test_ or sk_live_ prefixed) |
OPENFORT_WALLET_SECRET | Yes | P-256 ECDSA key for wallet authentication (base64 encoded) |
OPENFORT_WALLET_ADDRESS | No | Reuse existing wallet address instead of creating a new one |
Viem Signer
| Variable | Required | Description |
|---|---|---|
EVM_PRIVATE_KEY | Yes | Wallet 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-key2When 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.
Telemetry
| Variable | Default | Description |
|---|---|---|
ANONYMIZED_TELEMETRY | true (enabled) | Set to false to disable anonymized telemetry. See Telemetry for details |
DESPLEGA_TELEMETRY_ENV | production | Explicit telemetry environment tag. Set to development or test for intentional non-production telemetry. |
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.
| Variable | Default | Description |
|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT | none | Base 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_HEADERS | none | OTLP exporter headers, for example signoz-ingestion-key=<key>. Treated as sensitive by the secret scrubber |
OTEL_EXPORTER_OTLP_PROTOCOL | SDK default | Export protocol. Use http/protobuf for SigNoz Cloud |
OTEL_SERVICE_NAME | agent-swarm-api or agent-swarm-worker outside local compose | OpenTelemetry service.name. Set to agent-swarm everywhere to keep one service and split by agentswarm.service.role |
OTEL_RESOURCE_ATTRIBUTES | derived from NODE_ENV | Comma-separated resource attributes such as deployment.environment=local,env=local,service.namespace=agent-swarm |
OTEL_TRACE_POLL | OFF | When 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. |
Business-Use Instrumentation
Optional integration with @desplega.ai/business-use for tracking system invariants across the distributed API + worker architecture.
| Variable | Description |
|---|---|
BUSINESS_USE_API_KEY | API key from uvx business-use-core@latest init |
BUSINESS_USE_URL | BU 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:
SECRETS_ENCRYPTION_KEYenv var (base64-encoded 32 bytes)SECRETS_ENCRYPTION_KEY_FILEpointing at a file containing the base64 key<data-dir>/.encryption-keyfile on disk- 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_keyBack 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)
Related
- Harness Configuration — Configure Claude Code or pi-mono provider settings
- Deployment Guide — Production deployment with Docker Compose
- CLI Reference — Terminal commands that complement environment variable configuration
- MCP Tools Reference — Tools available to agents at runtime
MCP Tools Reference
Complete reference for Agent Swarm MCP tools — core and deferred tiers, annotations, Tool Search discovery, and capability-based organization
CLI Reference
Complete CLI reference for managing agents, tasks, and swarm configuration from the terminal — install via bunx or npx, manage worktrees, run agent commands, and configure your swarm without the dashboard UI