Configuration Guide
August 25, 2026 · View on GitHub
Complete reference for configuring The Construct CLI via config.toml.
Table of Contents
- Configuration Location
- Config File Structure
- Runtime Settings
- Sandbox Settings
- Network Settings
- Security Settings
- Agent Settings
- Daemon Settings
- Provider Configuration
- Package Management
- Environment Variables
- Examples
Configuration Location
The main configuration file lives at:
~/.config/construct-cli/config.toml
Configuration loading order:
- User config:
~/.config/construct-cli/config.toml - Defaults from
internal/config/defaults.go - Templates from
internal/templates/config.toml
Config File Structure
# Runtime Configuration
[runtime]
engine = "auto" # Container runtime: auto|podman|docker|container
auto_update_check = true # Check for updates automatically
update_check_interval = 86400 # Update check interval (seconds)
update_channel = "stable" # Release channel: stable|beta
# Sandbox Configuration
[sandbox]
mount_home = false # Mount home directory into container
forward_ssh_agent = true # Forward SSH agent socket
propagate_git_identity = true # Propagate git host identity
non_root_strict = false # Enforce non-root user in container
exec_as_host_user = true # Run commands as host user (when possible)
env_passthrough = ["GITHUB_TOKEN"] # Env vars to always pass through
shell = "/bin/bash" # Default shell in container
# Network Configuration
[network]
mode = "permissive" # Network mode: permissive|strict|offline
allowed_domains = [] # Domain allowlist (strict mode)
allowed_ips = [] # IP allowlist (strict mode)
blocked_domains = [] # Domain blocklist
blocked_ips = [] # IP blocklist
# Security Configuration
[security]
hide_secrets = false # Enable secret redaction (experimental)
hide_secrets_mask_style = "hash" # Mask style: hash|fixed
hide_secrets_deny_paths = [] # Force-scan these files
hide_secrets_allow_paths = [] # Never redact these files (dangerous!)
hide_secrets_passthrough_vars = [] # Never mask these env vars
hide_secrets_report = true # Show scan report
hide_git_dir = true # Hide .git directory
# Agent Configuration
[agents]
yolo_all = false # Enable all agents without confirmation
yolo_agents = [] # Enable specific agents without confirmation
clipboard_image_patch = true # Patch clipboard for image support
# Daemon Configuration
[daemon]
auto_start = true # Auto-start daemon on agent run
multi_paths_enabled = false # Enable multi-root daemon mounts
mount_paths = [] # Additional mount roots for daemon
# Provider Configuration
[claude.cc.provider]
ANTHROPIC_BASE_URL = "https://api.anthropic.com"
ANTHROPIC_AUTH_TOKEN = "${ANTHROPIC_API_KEY}"
Runtime Settings
Isolation Backend
[runtime]
backend = "auto" # auto (default) | container | podman | docker | microvm (experimental)
One key selects both the isolation technology and, for the container path, the OCI binary. It fails closed: a configured but missing backend is a hard error, never a silent fallback.
Options:
auto(default): auto-detect the container runtime, prioritycontainer>podman>dockercontainer: pin the macOS native runtime (macOS 14+)podman: pin Podmandocker: pin Docker (OrbStack on macOS, then Docker Desktop). Pinning sets the priority order, not a hard requirement: if the pinned binary is unavailable, detection falls through to the others.microvm(experimental): microsandbox microVMs (a dedicated Linux guest kernel per agent sandbox). Requiresmsbinstalled (curl -fsSL https://msb.sh | sh), Apple Silicon macOS (Hypervisor.framework) or Linux with KVM (/dev/kvm). Allocates 4 vCPUs and 4096 MiB RAM by default. Bridges (clipboard, host-exec, SSH agent proxy, loopback dev-site forwarding) and network enforcement modes (permissive, strict, offline) are fully supported. The daemon sandbox automatically transitions images via GHCR pull or local docker save/load. Every run executes in the persistent daemon sandbox: with[daemon] multi_paths_enabled = trueandmount_pathsset to your project roots, the sandbox is created once and reused across all of them, so the guest is not re-initialized when you switch directories (see Daemon Settings). Complete design, benchmarks, and details: docs/ARCHITECTURE-DESIGN.md.
Migrating from the old engine key: engine and backend merged into the single backend key. On first run, Construct rewrites a legacy engine key in place (comments preserved): a pinned engine = "podman" becomes backend = "podman", and engine = "auto" is dropped. If you had explicitly set backend = "docker" with no engine key and want auto-detection back, set backend = "auto" (the old explicit docker value now pins the Docker binary).
Update Management
[runtime]
auto_update_check = true # Enable automatic update checks
update_check_interval = 86400 # Check interval in seconds (24h)
update_channel = "stable" # Release channel: stable|beta
Channels:
stable: Production releases onlybeta: Includes pre-release features
Sandbox Settings
Home Directory Mount
[sandbox]
mount_home = false
When false (default):
- Agent cannot access your home directory
- More secure, prevents accidental file access
- Recommended for most use cases
When true:
- Agent can access your home directory
- Useful for workflows requiring home directory access
- Security implication: Agent can read your files
Automatic Host Mounts
Independent of mount_home, Construct adds a few host→container bind-mounts automatically when the host path exists (no configuration needed):
- Global gitignore →
/home/construct/.config/git/ignore(read-only), so agents inherit your host ignore rules. - qmd model cache (
~/.cache/qmd/models) →/home/construct/.cache/qmd/models(read-write), so the qmd semantic-search backend reuses already-downloaded GGUF models (~1.5GB) instead of re-fetching them per container. Read-write lets any lazily-fetched model write back to the shared host cache.
These are skipped silently when the host path is absent.
SSH Agent Forwarding
[sandbox]
forward_ssh_agent = true
Automatically:
- Detects SSH agent socket
- Mounts into container
- Sets
SSH_AUTH_SOCKenvironment variable
Benefits:
- Agent can use your SSH keys
- No manual key copying
- Secure forwarding
Fallback:
construct sys ssh-import # Import host keys manually
Environment Variable Passthrough
[sandbox]
env_passthrough = [
"GITHUB_TOKEN",
"CONTEXT7_API_KEY",
"CUSTOM_API_KEY"
]
env_passthrough_prefixes = [
"CNSTR_"
]
Behavior:
- Listed vars always passed to container
- Prefix matching:
CNSTR_*passes all vars with that prefix - Useful for API keys and custom configuration
Host Service Bridging (host_service_env)
Inject an environment variable whose value points at a service reachable from the sandbox, with automatic rewriting of host-loopback addresses to the container gateway.
[sandbox]
host_service_env = [
"AGENTMEMORY_URL=http://192.168.10.250:3111",
]
Format: VAR_NAME=URL. Each entry becomes a line in the generated docker-compose.override.yml environment: block.
Rewrite rule: localhost and 127.0.0.1 in the value are replaced with host.docker.internal (the container gateway that routes back to the host). Any other host (a LAN IP, a DNS name, a hostname) passes through unchanged.
When to use this instead of env_passthrough. env_passthrough forwards the host env value verbatim. That is wrong for a service URL when the host value uses a name that does not resolve inside the container. Example: the host may reach a NAS via AGENTMEMORY_URL=http://whitebox:3111, where whitebox is a host-side mDNS name. That name does not resolve inside the sandbox, so passthrough would inject a URL the agent cannot reach. host_service_env lets you declare a container-routable address (the raw LAN IP) that differs from the host's hostname form. Rules of thumb:
- Secrets / tokens ->
env_passthrough(the value is host-independent; you want it hot). - Service URLs that already resolve inside the sandbox -> either mechanism works.
- Service URLs using a host-only name ->
host_service_envwith a container-routable address.
⚠ Lifecycle: cold, not hot. This is the common trap. env_passthrough vars are injected as -e flags at every agent launch, so they self-heal on each run. host_service_env vars are written into the compose environment: block and baked into the daemon container only at container creation. Editing host_service_env regenerates the override file, but a running construct-cli-daemon container keeps the environment it was created with. A changed value will not take effect until the container is recreated:
construct sys daemon restart # StopContainer removes it, Start() does compose run -d -> fresh container
Verify the live value after restart:
docker exec construct-cli-daemon printenv AGENTMEMORY_URL
Symptom of a stale host_service_env value. An agent reports a service as unreachable or "off", and its effective URL is the extension's fallback default (commonly http://host.docker.internal:<port>), not the value in your config. That means the container predates the current override. Run construct sys daemon restart.
For copy-paste recipes wiring real services into the sandbox (agentmemory on a LAN/NAS, Asana, Slack, Jira/Confluence, Context7, Brave, Claude-compatible provider aliases), see Services & Integrations.
Host Exec Bridge (Proxy Binaries to the Host)
Run selected binaries on the host machine when the agent invokes them from inside the sandbox, instead of in the container. The agent sees them on PATH and calls them normally; a shim proxies each call to a host-side bridge that runs the real binary as your host user.
[sandbox]
host_binaries = ["wicket"]
⚠ Security: each listed binary runs on the host with full container-controlled argv. Only list binaries you trust with that. Declaring docker grants effective host root to the agent; aws/kubectl likewise. The bridge is token-gated and allowlist-pinned, but argv is not filtered.
- Off when empty (default). No bridge starts, zero attack surface.
- Requires
construct buildafter first enabling, so the container shim is baked into the image. - Non-interactive only: no controlling terminal/PTY. Pipe stdin (one-shot) works; interactive prompts do not. Pass
--no-interactive/--json/--yesflags where available. - A startup banner (
⚠ host exec enabled: ...) confirms when active. - Full details: Host Exec Bridge.
Terminal Identity Forwarding (Automatic)
Host terminal-identity markers (KITTY_WINDOW_ID, GHOSTTY_RESOURCES_DIR, TERM_PROGRAM) are forwarded into the container automatically so in-container TUIs and pi extensions can detect the outer terminal (for example, kitty-graphics inline image rendering). No config is needed; it applies on both the direct run and daemon paths.
TERM is intentionally not forwarded, because forwarding e.g. xterm-kitty into a container that lacks the matching terminfo breaks ncurses apps (less, vim, btop). If you need it, add TERM to env_passthrough and install ncurses-term (or kitty-terminfo) in the image.
Host Loopback Forwarding (Browser -> Host Dev Sites)
Chromium hardcodes localhost and *.localhost to 127.0.0.1 (RFC 6761), bypassing /etc/hosts, DNS, dnsmasq, and --host-resolver-rules. A DNS-layer fix therefore cannot let a headless browser (agent-browser) reach host dev servers like http://hyperpress.localhost, even though non-browser tools (curl, git, MCP) reach them fine. Construct relays those connections instead.
[sandbox]
host_loopback_ports = [80, 443] # default; same port both sides
For each listed port, entrypoint.sh starts a blind TCP relay on the container's 127.0.0.1:<port> that forwards to host.docker.internal:<port>. Blind relay preserves the HTTP Host header and TLS SNI, so host vhost routers (valet, Hyperpress) and certs see the real hostname. The container is granted cap_add: NET_BIND_SERVICE so the non-root user's socat can bind ports below 1024 (the file cap is applied at runtime as root in the entrypoint, since BuildKit blocks file-cap writes at build time).
- Default
[80, 443]covershttp://andhttps://on the standard ports. - Add non-standard ports (e.g.
3000) as needed. - Empty list disables the feature (no relay, no
NET_BIND_SERVICEcap). - Linux caveat: the host-gateway is the bridge IP, so host services must bind
0.0.0.0/bridge (not127.0.0.1-only) to be reachable. macOS host-gateway already routes to host127.0.0.1.
Network Settings
Network Modes
[network]
mode = "permissive" # Options: permissive, strict, offline
Modes:
Permissive (default):
- All network traffic allowed
- Domain/IP blocklists still enforced
- Good for general development
Strict:
- Only allowlisted traffic allowed
- Must configure
allowed_domainsand/orallowed_ips - Best security for sensitive work
Offline:
- No network access
- Agent cannot make external API calls
- Maximum security for air-gapped environments
Allowlists (Strict Mode)
[network]
mode = "strict"
allowed_domains = [
"*.anthropic.com",
"*.openai.com",
"*.googleapis.com",
"api.z.ai"
]
allowed_ips = [
"1.1.1.1/32", # Cloudflare DNS
"8.8.8.8/32" # Google DNS
]
Patterns:
- Wildcards supported:
*.example.com - CIDR notation for IPs:
192.168.1.0/24 - Exact matches:
api.example.com
Blocklists
[network]
blocked_domains = [
"*.malicious-site.example",
"*.phishing.attempt.com"
]
blocked_ips = [
"192.168.100.100/32",
"203.0.113.0/24"
]
Priority: Blocklists take precedence over allowlists.
Security Settings
Secret Redaction (Experimental)
⚠️ Requires environment gate: CONSTRUCT_EXPERIMENT_HIDE_SECRETS=1
[security]
hide_secrets = true
hide_secrets_mask_style = "hash"
hide_secrets_deny_paths = ["**/secrets.yml"]
hide_secrets_allow_paths = ["~/.aws/credentials"]
hide_secrets_passthrough_vars = ["PUBLIC_API_URL"]
hide_secrets_report = true
hide_git_dir = true
See: Hide Secrets Guide for complete documentation.
Agent Settings
Confirmation Bypass
[agents]
yolo_all = false # Bypass confirmation for ALL agents
yolo_agents = [ # Bypass confirmation for specific agents
"claude",
"agy"
]
Security implication:
yolo_all = true: Agents run without confirmation- Use only in trusted environments
- Specific agent bypass is safer than global bypass
Clipboard Support
[agents]
clipboard_image_patch = true # Patch agents for image clipboard support
Required for: Image paste support in Claude, Copilot, Antigravity, etc.
Daemon Settings
Auto-Start
[daemon]
auto_start = true # Auto-start daemon on first agent run
Benefits:
- Faster subsequent agent startups
- Persistent daemon across multiple runs
- Resource efficient
Multi-Root Mounts
[daemon]
multi_paths_enabled = false
mount_paths = []
Enable when: Working with multiple projects simultaneously
MicroVM backend: with backend = "microvm", multi-root mounts are the preferred setup. Every run goes through the persistent daemon sandbox; with your project roots configured in mount_paths, the sandbox is created once and reused for all of them. Switching to a directory outside the configured roots stops with an actionable error instead of recreating the sandbox, so guest state (installed packages, agent CLIs) survives. Changing mount_paths itself recreates the sandbox once, on the next run.
[daemon]
multi_paths_enabled = true
mount_paths = [
"~/Dev/Projects",
"/work/client-repos"
]
Provider Configuration
Claude Code Providers
Configure alternative Claude API endpoints:
[claude.cc.zai]
ANTHROPIC_BASE_URL = "https://api.z.ai/api/anthropic"
ANTHROPIC_AUTH_TOKEN = "${CNSTR_ZAI_API_KEY}"
API_TIMEOUT_MS = "3000000"
[claude.cc.minimax]
ANTHROPIC_BASE_URL = "https://api.minimax.io/anthropic"
ANTHROPIC_AUTH_TOKEN = "${CNSTR_MINIMAX_API_KEY}"
See: Providers Guide for complete provider reference.
Package Management
User-Defined Packages
Configure additional packages in packages.toml:
[brew]
packages = [
"node",
"python@3.11"
]
[npm]
packages = [
"typescript",
"eslint"
]
See: Packages Guide for detailed package management.
Environment Variables
Construct-Specific Variables
Runtime overrides:
CONSTRUCT_EXPERIMENT_HIDE_SECRETS=1 # Enable hide-secrets mode
CONSTRUCT_CONFIG_DIR=/custom/path # Custom config directory
CONSTRUCT_DATA_DIR=/custom/data # Custom data directory
Provider Variables
Common provider keys (auto-forwarded):
ANTHROPIC_API_KEYOPENAI_API_KEYANTIGRAVITY_API_KEYCNSTR_*(custom prefix)
Usage:
[claude.cc.custom]
ANTHROPIC_AUTH_TOKEN = "${CUSTOM_API_KEY}"
Examples
Minimal Configuration
[runtime]
engine = "auto"
[network]
mode = "permissive"
Development Setup
[runtime]
engine = "podman"
auto_update_check = true
[sandbox]
mount_home = false
forward_ssh_agent = true
env_passthrough = ["GITHUB_TOKEN", "NPM_TOKEN"]
[network]
mode = "permissive"
blocked_domains = ["*.malicious-site.example"]
[agents]
clipboard_image_patch = true
High-Security Setup
[runtime]
engine = "podman"
[sandbox]
mount_home = false
forward_ssh_agent = false
exec_as_host_user = true
[network]
mode = "strict"
allowed_domains = [
"*.anthropic.com",
"*.openai.com",
"api.z.ai"
]
blocked_ips = ["0.0.0.0/8"] # Block local network
[security]
hide_secrets = true # Requires CONSTRUCT_EXPERIMENT_HIDE_SECRETS=1
hide_git_dir = true
[agents]
yolo_all = false
Offline Development
[runtime]
engine = "container" # macOS native runtime
[network]
mode = "offline" # No network access
[sandbox]
mount_home = true # Need home for offline files
env_passthrough = ["OFFLINE_MODE"]
Configuration Migration
Construct automatically migrates your config when upgrading:
- Backs up existing config to
config.toml.backup - Merges new defaults with your settings
- Preserves all your customizations
- Validates configuration after merge
Manual migration:
construct sys config --migrate
Next Steps
Getting Help
construct sys doctor # Check configuration and runtime
construct sys config # Open config in editor
construct --help # Show all options