ACTION_TIMEOUT_MS=15000
AGENT_JOB_WORKER_COUNT=1
ALLOWED_HOSTS
ANTHROPIC_API_KEY
ANTHROPIC_BASE_URL=https://api.anthropic.com/v1
ANTHROPIC_VERSION=2023-06-01
API_BEARER_TOKEN
API_BEARER_TOKENSNamed credentials: `alice:token-a,bob:token-b`. A request authenticating with one of these has a verified operator identity, so X-Operator-Id can no longer claim to be someone else. Sessions and auth profiles then belong to the operator who created them, and other operators neither see nor use them, down to their approvals, agent jobs, cron jobs and /artifacts files. The shared API_BEARER_TOKEN ab
Named credentials: `alice:token-a,bob:token-b`. A request authenticating with one of these has a verified operator identity, so X-Operator-Id can no longer claim to be someone else. Sessions and auth profiles then belong to the operator who created them, and other operators neither see nor use them, down to their approvals, agent jobs, cron jobs and /artifacts files. The shared API_BEARER_TOKEN ab
API_BIND_SCOPEloopback | exposed. The base compose publishes the API on 127.0.0.1 and declares `loopback`, which is what lets it run with no token. Set `exposed` whenever the API is reachable from anywhere else — the controller then refuses to start without an API_BEARER_TOKEN of at least 32 characters.
loopback | exposed. The base compose publishes the API on 127.0.0.1 and declares `loopback`, which is what lets it run with no token. Set `exposed` whenever the API is reachable from anywhere else — the controller then refuses to start without an API_BEARER_TOKEN of at least 32 characters.
API_PORT=8000
APP_ENV=development
APPROVAL_ROOT=/data/approvals
APPROVAL_TTL_MINUTESApproval workflow
APPROVAL_WEBHOOK_SECRET
APPROVAL_WEBHOOK_URLApproval webhooks (optional — called when a tool needs human approval)
Approval webhooks (optional — called when a tool needs human approval)
ARTIFACT_RETENTION_HOURS=168
ARTIFACT_ROOTStorage roots (rarely need changing when using docker-compose volumes)
Storage roots (rarely need changing when using docker-compose volumes)
AUDIT_MAX_EVENTS=10000
AUDIT_ROOT=/data/audit
AUTH_RETENTION_HOURS=168
AUTH_ROOT
AUTH_STATE_ENCRYPTION_KEY
AUTH_STATE_MAX_AGE_HOURS=72
AUTONOMOUS_APPROVALSAutonomous mode: let agents approve their own actions instead of waiting for an operator. The built-in agent approves and carries on, and MCP clients may call browser.approve_approval. This covers every kind, payments and destructive actions included. Each approval is still created and recorded, marked decided_via=agent in the audit log and witness chain.
Autonomous mode: let agents approve their own actions instead of waiting for an operator. The built-in agent approves and carries on, and MCP clients may call browser.approve_approval. This covers every kind, payments and destructive actions included. Each approval is still created and recorded, marked decided_via=agent in the audit log and witness chain.
BROWSER_HEIGHT=800
BROWSER_URL=about:blank
BROWSER_WIDTH=1280
BROWSER_WS_ENDPOINT_FILE=/data/browser-profile/browser-ws-endpoint.txt
CDP_CONNECT_URLCDP connect mode — attach to an existing Chrome (--remote-debugging-port)
CDP connect mode — attach to an existing Chrome (--remote-debugging-port)
CLAUDE_AUTH_MODE=api
CLAUDE_CLI_MODEL
CLAUDE_CLI_PATH=claude
CLAUDE_MODEL=claude-sonnet-4-6
CLEANUP_INTERVAL_SECONDS=3600
CLEANUP_ON_STARTUP=true
CLI_HOME=/data/cli-home
CLI_HOST_HOME=/home/youruserOptional: when using docker-compose.host-subscriptions.yml, point this at the host home that already contains your signed-in CLI state.
Optional: when using docker-compose.host-subscriptions.yml, point this at the host home that already contains your signed-in CLI state.
CODEX_BRIDGE_ALLOW_HOST_EXEC=false
CODEX_SANDBOX_MODE=read-onlyHow the Codex CLI may execute. read-only | workspace-write | danger-full-access. Codex is only asked for a JSON decision about a screenshot here, so read-only is enough. CODEX_BRIDGE_ALLOW_HOST_EXEC=true restores the old behaviour of passing --dangerously-bypass-approvals-and-sandbox: a model decision can then run any command on the host. Only inside an environment that is itself sandboxed.
How the Codex CLI may execute. read-only | workspace-write | danger-full-access. Codex is only asked for a JSON decision about a screenshot here, so read-only is enough. CODEX_BRIDGE_ALLOW_HOST_EXEC=true restores the old behaviour of passing --dangerously-bypass-approvals-and-sandbox: a model decision can then run any command on the host. Only inside an environment that is itself sandboxed.
COMPLIANCE_MANIFEST_PATHCompliance Templates COMPLIANCE_TEMPLATE=HIPAA # HIPAA | SOC2 | GDPR | PCI-DSS
Compliance Templates COMPLIANCE_TEMPLATE=HIPAA # HIPAA | SOC2 | GDPR | PCI-DSS
CONNECT_RETRIES=60
CONNECT_RETRY_DELAY_SECONDS=1
CONTROLLER_ALLOWED_HOSTSHost names the controller answers to. Left empty on a controller with no API_BEARER_TOKEN, only loopback names are accepted (DNS-rebinding guard).
Host names the controller answers to. Left empty on a controller with no API_BEARER_TOKEN, only loopback names are accepted (DNS-rebinding guard).
CRON_MAX_JOBS
CRON_STORE_PATHCron / webhook trigger store
Cron / webhook trigger store
CURATOR_ENABLEDSkills Curator — reviews each closed session with an LLM. Off by default: it makes a paid model call on every session close.
Skills Curator — reviews each closed session with an LLM. Off by default: it makes a paid model call on every session close.
DEEPSEEK_API_KEYDeepSeek (text-only; driven from the DOM/accessibility outline)
DeepSeek (text-only; driven from the DOM/accessibility outline)
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
DEEPSEEK_MODEL=deepseek-chat
DEFAULT_PROXY_PASSWORD
DEFAULT_PROXY_SERVERProxy routing (optional — per-session override also supported in POST /sessions)
Proxy routing (optional — per-session override also supported in POST /sessions)
DEFAULT_PROXY_USERNAME
DEFAULT_VIEWPORT_HEIGHT
DEFAULT_VIEWPORT_WIDTHViewport defaults for new sessions
Viewport defaults for new sessions
ENABLE_TRACING=true
FORCE_GOVERNED_APPROVALSMake governed approvals the server's rule rather than each agent's choice. Off, a write asks for approval only when the caller passes workflow_profile=governed (the default is fast). On, approval is required whatever the caller passed, and a caller cannot downgrade it to fast, for: - every MCP tool with a governed kind (16 in the full tool profile, such as browser.execute_action, browser.set_cooki
Make governed approvals the server's rule rather than each agent's choice. Off, a write asks for approval only when the caller passes workflow_profile=governed (the default is fast). On, approval is required whatever the caller passed, and a caller cannot downgrade it to fast, for: - every MCP tool with a governed kind (16 in the full tool profile, such as browser.execute_action, browser.set_cooki
GEMINI_API_KEY
GEMINI_AUTH_MODE=api
GEMINI_BASE_URL=https://generativelanguage.googleapis.com/v1beta
GEMINI_CLI_MODEL
GEMINI_CLI_PATH=gemini
GEMINI_MODEL=gemini-3.5-flash
HARNESS_EXECUTOR_MODEL
HARNESS_EXPLORER_MODELModels recorded for cost provenance; the executor one is the minimum tier on staged skills.
Models recorded for cost provenance; the executor one is the minimum tier on staged skills.
HARNESS_ROOTConvergence harness (docs/convergence-harness.md)
Convergence harness (docs/convergence-harness.md)
HARNESS_UV_COMMANDPinned command for the UV verifier adapter; empty leaves it unverified.
Pinned command for the UV verifier adapter; empty leaves it unverified.
HARNESS_VERIFIERprogrammatic | uv | ensemble
programmatic | uv | ensemble
HARNESS_VERIFIER_MODEL
HUMAN_TYPING_MAX_DELAY_MS=130
HUMAN_TYPING_MIN_DELAY_MS=40Interaction pacing
INSTALL_AGENT_CLIS=false*_AUTH_MODE=cli runs the codex/claude/gemini CLIs inside the controller. The image includes them (plus Node.js, ~750 MB) only when built with this set to true: `docker compose build controller` after changing it.
*_AUTH_MODE=cli runs the codex/claude/gemini CLIs inside the controller. The image includes them (plus Node.js, ~750 MB) only when built with this set to true: `docker compose build controller` after changing it.
ISOLATED_BROWSER_BIND_HOST=127.0.0.1
ISOLATED_BROWSER_CONTAINER_PREFIX=browser-session
ISOLATED_BROWSER_CPUS=0
ISOLATED_BROWSER_IMAGE=auto-browser-browser-node:latest
ISOLATED_BROWSER_KEEP_CONTAINERS=false
ISOLATED_BROWSER_MEM_LIMIT=4gResource caps for per-session browser containers (docker_ephemeral mode). MEM_LIMIT accepts Docker size strings ("" disables). PIDS_LIMIT/CPUS of 0 disable the cap.
Resource caps for per-session browser containers (docker_ephemeral mode). MEM_LIMIT accepts Docker size strings ("" disables). PIDS_LIMIT/CPUS of 0 disable the cap.
ISOLATED_BROWSER_NETWORK
ISOLATED_BROWSER_PIDS_LIMIT=2048
ISOLATED_BROWSER_WAIT_TIMEOUT_SECONDS=45
ISOLATED_DOCKER_HOST
ISOLATED_HOST_DATA_ROOT
ISOLATED_TAKEOVER_HOST=127.0.0.1
ISOLATED_TAKEOVER_PATH=/vnc.html?autoconnect=true&resize=scale
ISOLATED_TAKEOVER_SCHEME=http
ISOLATED_TUNNEL_ACCESS_MODE=private
ISOLATED_TUNNEL_ENABLED=false
ISOLATED_TUNNEL_HOST
ISOLATED_TUNNEL_INFO_INTERVAL_SECONDS=10
ISOLATED_TUNNEL_INFO_ROOT=/data/tunnels/sessions
ISOLATED_TUNNEL_KEY_PATH
ISOLATED_TUNNEL_KNOWN_HOSTS_PATH=/data/ssh/known_hosts
ISOLATED_TUNNEL_LOCAL_HOST=host.docker.internalOptional override. By default the controller tunnels directly to the isolated browser container over the Docker network instead of hairpinning through a host-published port.
Optional override. By default the controller tunnels directly to the isolated browser container over the Docker network instead of hairpinning through a host-published port.
ISOLATED_TUNNEL_PORT=22
ISOLATED_TUNNEL_PUBLIC_HOST
ISOLATED_TUNNEL_PUBLIC_SCHEME=http
ISOLATED_TUNNEL_REMOTE_BIND_ADDRESS=127.0.0.1
ISOLATED_TUNNEL_REMOTE_PORT_END=16240
ISOLATED_TUNNEL_REMOTE_PORT_START=16181
ISOLATED_TUNNEL_SERVER_ALIVE_COUNT_MAX=3
ISOLATED_TUNNEL_SERVER_ALIVE_INTERVAL=30
ISOLATED_TUNNEL_STARTUP_GRACE_SECONDS=1
ISOLATED_TUNNEL_STRICT_HOST_KEY_CHECKING
ISOLATED_TUNNEL_USER
JOB_STORE_ROOT=/data/jobs
LOG_LEVEL=INFOLogging
MAX_SESSIONS=1
MCP_ALLOWED_ORIGINS
MCP_SESSION_STORE_PATHMCP session persistence
MCP_TOOL_PROFILE=curated
MEMORY_ENABLED=true
MEMORY_ROOT=/data/memoryAgent Memory Profiles
MESH_ENABLEDMesh (peer-to-peer delegation; off by default)
Mesh (peer-to-peer delegation; off by default)
MESH_IDENTITY_DIR
MESH_PEERS_PATH
MESH_TIMESTAMP_WINDOW
METRICS_ENABLED=true
MINIMAX_API_KEYMiniMax
MINIMAX_BASE_URL=https://api.minimax.io/v1
MINIMAX_MODEL=MiniMax-M3
MODEL_MAX_RETRIES=2
MODEL_REQUEST_TIMEOUT_SECONDS=60
MODEL_RETRY_BACKOFF_SECONDS=1
NETWORK_INSPECTOR_BODY_MAX_BYTES
NETWORK_INSPECTOR_CAPTURE_BODIES
NETWORK_INSPECTOR_ENABLEDNetwork inspector
NETWORK_INSPECTOR_MAX_ENTRIES
NOVNC_ALLOWED_HOSTSnoVNC accepts a WebSocket only from the noVNC page's own origin on a loopback name, so pages the browser visits cannot read or drive the desktop. Host names (not loopback) that the takeover URL uses, e.g. a LAN IP or bastion name:
noVNC accepts a WebSocket only from the noVNC page's own origin on a loopback name, so pages the browser visits cannot read or drive the desktop. Host names (not loopback) that the takeover URL uses, e.g. a LAN IP or bastion name:
NOVNC_ALLOWED_ORIGINSFull origins to accept when a proxy in front of port 6080 rewrites Host:
Full origins to accept when a proxy in front of port 6080 rewrites Host:
NOVNC_PORT=6080
OCR_ENABLED=true
OCR_LANGUAGE=eng
OCR_MAX_BLOCKS=20
OCR_SKIP_WHEN_TEXT_AVAILABLE=trueSkip OCR when the page already exposes readable text; false always runs it.
Skip OCR when the page already exposes readable text; false always runs it.
OCR_TEXT_LIMIT=1200
OPENAI_API_KEY
OPENAI_AUTH_MODE=api
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_CLI_MODEL
OPENAI_CLI_PATH=codex
OPENAI_COMPATIBLE_API_KEYAny other OpenAI-compatible endpoint: self-hosted Ollama / vLLM / LM Studio, Azure OpenAI, Together, Groq, Fireworks, ... Set base URL + model.
Any other OpenAI-compatible endpoint: self-hosted Ollama / vLLM / LM Studio, Azure OpenAI, Together, Groq, Fireworks, ... Set base URL + model.
OPENAI_COMPATIBLE_BASE_URL
OPENAI_COMPATIBLE_MODEL
OPENAI_HOST_BRIDGE_SOCKET=/data/host-bridge/codex.sock
OPENAI_MODEL=gpt-6.1-sol
OPENROUTER_API_KEYOpenAI-compatible providers (one generic adapter -> effectively every model) Set an API key to enable a provider. base_url has a sensible default; set the model where marked. Vision (screenshots) is on for all except DeepSeek (text). OpenRouter: one key proxies ~every frontier model (Claude, GPT, Gemini, Grok, DeepSeek, Llama, Mistral, Qwen, ...). Pick the model, e.g. anthropic/claude-3.7-sonnet.
OpenAI-compatible providers (one generic adapter -> effectively every model) Set an API key to enable a provider. base_url has a sensible default; set the model where marked. Vision (screenshots) is on for all except DeepSeek (text). OpenRouter: one key proxies ~every frontier model (Claude, GPT, Gemini, Grok, DeepSeek, Llama, Mistral, Qwen, ...). Pick the model, e.g. anthropic/claude-3.7-sonnet.
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
OPENROUTER_MODEL
OPERATOR_ID_HEADER=X-Operator-Id
OPERATOR_NAME_HEADER=X-Operator-Name
PERCEPTION_PRESET_DEFAULTPerception preset for /observe (fast | normal | rich)
Perception preset for /observe (fast | normal | rich)
PII_SCRUB_ACTION_SCREENSHOTSRedacting the before/after snapshot of every action runs OCR twice per action, the biggest cost an action has. false makes actions much faster and leaves those snapshots unredacted; observation screenshots stay redacted.
Redacting the before/after snapshot of every action runs OCR twice per action, the biggest cost an action has. false makes actions much faster and leaves those snapshots unredacted; observation screenshots stay redacted.
PII_SCRUB_AUDIT_REPORT
PII_SCRUB_CONSOLE
PII_SCRUB_ENABLEDPII scrubbing (all on by default)
PII scrubbing (all on by default)
PII_SCRUB_NETWORK
PII_SCRUB_REPLACEMENTPII_SCRUB_PATTERNS= # blank = all 20 built-in pattern classes except generic_hex_token
PII_SCRUB_PATTERNS= # blank = all 20 built-in pattern classes except generic_hex_token
PII_SCRUB_SCREENSHOTScreenshot redaction paints over OCR-detected words, so it needs the tesseract binary (the Docker image ships it). Without it screenshots are returned unscrubbed and /readyz warns. Page content (HTML, DOM, cookies, raw CDP) is never scrubbed: docs/deployment.md, "What PII scrubbing covers".
Screenshot redaction paints over OCR-detected words, so it needs the tesseract binary (the Docker image ships it). Without it screenshots are returned unscrubbed and /readyz warns. Page content (HTML, DOM, cookies, raw CDP) is never scrubbed: docs/deployment.md, "What PII scrubbing covers".
PROXY_PERSONA_FILEProxy persona file — JSON map of named proxy configs
Proxy persona file — JSON map of named proxy configs
REDIS_URL
REMOTE_ACCESS_INFO_PATH=/data/tunnels/reverse-ssh.json
REMOTE_ACCESS_STALE_AFTER_SECONDS=45
REQUEST_RATE_LIMIT_ENABLED=true
REQUEST_RATE_LIMIT_EXEMPT_PATHS=/healthz,/readyz,/docs,/openapi.json,/redoc,/artifacts,/metrics
REQUEST_RATE_LIMIT_MAX_BUCKETS=4096
REQUEST_RATE_LIMIT_REQUESTS=120
REQUEST_RATE_LIMIT_WINDOW_SECONDS=60
REQUIRE_APPROVAL_FOR_UPLOADS=true
REQUIRE_AUTH_STATE_ENCRYPTION=false
REQUIRE_OPERATOR_ID=false
REVERSE_SSH_ACCESS_MODE=private
REVERSE_SSH_ALLOW_NONLOCAL_BIND=false
REVERSE_SSH_HOSTOptional reverse-SSH profile for private remote access. Start with: docker compose --profile reverse-ssh up --build The controller will read REMOTE_ACCESS_INFO_PATH and automatically prefer the tunnel's public_takeover_url/public_api_url when that metadata file exists.
Optional reverse-SSH profile for private remote access. Start with: docker compose --profile reverse-ssh up --build The controller will read REMOTE_ACCESS_INFO_PATH and automatically prefer the tunnel's public_takeover_url/public_api_url when that metadata file exists.
REVERSE_SSH_INFO_INTERVAL_SECONDS=15
REVERSE_SSH_KEY_PATH
REVERSE_SSH_KNOWN_HOSTS_PATH=/ssh/known_hosts
REVERSE_SSH_PORT=22
REVERSE_SSH_PUBLIC_API_URL
REVERSE_SSH_PUBLIC_HOST
REVERSE_SSH_PUBLIC_SCHEME=http
REVERSE_SSH_PUBLIC_TAKEOVER_URL
REVERSE_SSH_REMOTE_API_PORT=18000
REVERSE_SSH_REMOTE_BIND_ADDRESS=127.0.0.1
REVERSE_SSH_REMOTE_NOVNC_PORT=16080
REVERSE_SSH_SERVER_ALIVE_COUNT_MAX=3
REVERSE_SSH_SERVER_ALIVE_INTERVAL=30
REVERSE_SSH_STALE_AFTER_SECONDS=45
REVERSE_SSH_STRICT_HOST_KEY_CHECKING
REVERSE_SSH_USER
SESSION_ISOLATION_MODE=shared_browser_node
SESSION_STORE_REDIS_PREFIX=auto_browser:sessions
SESSION_STORE_ROOT=/data/sessions
SHADOW_BROWSE_ENABLEDShadow browsing — reopen a session in a headed Chromium inside the controller, for debugging on a host run. Off by default: that browser runs unsandboxed next to the controller's secrets, and the Docker image has no browser for it.
Shadow browsing — reopen a session in a headed Chromium inside the controller, for debugging on a host run. Off by default: that browser runs unsandboxed next to the controller's secrets, and the Docker image has no browser for it.
SHARE_TOKEN_SECRETShared session links (HMAC-signed observer tokens)
Shared session links (HMAC-signed observer tokens)
SHARE_TOKEN_TTL_MINUTES
SKILLS_STAGING_ROOTCURATOR_PROVIDER=claude # claude | openai | gemini CURATOR_MODEL= # override default model per provider
CURATOR_PROVIDER=claude # claude | openai | gemini CURATOR_MODEL= # override default model per provider
SSE_KEEPALIVE_SECONDSSSE keepalive interval in seconds (prevents proxy timeouts)
SSE keepalive interval in seconds (prevents proxy timeouts)
STATE_DB_PATH
STEALTH_ENABLEDBrowser fingerprint / stealth
Browser fingerprint / stealth
STEALTH_PROFILEStealth (humanized browser actions; off by default) off — raw Playwright speed light — timing jitter + Bézier mouse (recommended) aggressive — full fingerprint noise + tight human mimicry
Stealth (humanized browser actions; off by default) off — raw Playwright speed light — timing jitter + Bézier mouse (recommended) aggressive — full fingerprint noise + tight human mimicry
TAKEOVER_URL=http://127.0.0.1:6080/vnc.html?autoconnect=true&resize=scale
TYPING_DELAY_MS=20
UPLOAD_RETENTION_HOURS=168
UPLOAD_ROOT
VISION_MODEL=claude-haiku-4-5-20251001
VNC_PASSWORDOptional VNC authentication for noVNC takeover and raw VNC, on the shared browser-node and on isolated session containers. Needed only if those ports are published beyond loopback. The VNC protocol uses the first 8 characters.
Optional VNC authentication for noVNC takeover and raw VNC, on the shared browser-node and on isolated session containers. Needed only if those ports are published beyond loopback. The VNC protocol uses the first 8 characters.
VNC_PORT=5900
WITNESS_ENABLED=true
WITNESS_PROTECTION_MODE_DEFAULT=normal
WITNESS_REMOTE_API_KEY
WITNESS_REMOTE_REQUIRED_FOR_CONFIDENTIAL=false
WITNESS_REMOTE_TENANT_ID
WITNESS_REMOTE_TIMEOUT_SECONDS=0.75
WITNESS_REMOTE_URL
WITNESS_REMOTE_VERIFY_TLS=true
WITNESS_ROOT=/data/witness
XAI_API_KEYxAI (Grok)
XAI_BASE_URL=https://api.x.ai/v1
XAI_MODEL=grok-4
0 comments