Complete reference for configuring The Construct CLI via config.toml.
- Configuration Location
- Config File Structure
- Runtime Settings
- Sandbox Settings
- Network Settings
- Security Settings
- Agent Settings
- Daemon Settings
- Provider Configuration
- Package Management
- Environment Variables
- Examples
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
# 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]
engine = "auto" # Options: auto, podman, docker, containerOptions:
auto: Auto-detect available runtime (recommended)podman: Use Podman explicitlydocker: Use Docker explicitlycontainer: Use macOS native runtime (macOS 14+)
Runtime detection order:
- macOS native container runtime (if available)
- Podman
- Docker (OrbStack on macOS, then Docker Desktop)
[runtime]
auto_update_check = true # Enable automatic update checks
update_check_interval = 86400 # Check interval in seconds (24h)
update_channel = "stable" # Release channel: stable|betaChannels:
stable: Production releases onlybeta: Includes pre-release features
[sandbox]
mount_home = falseWhen 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
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.
[sandbox]
forward_ssh_agent = trueAutomatically:
- 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[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
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.
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.
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 sidesFor 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]
mode = "permissive" # Options: permissive, strict, offlineModes:
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
[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
[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.
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 = trueSee: Hide Secrets Guide for complete documentation.
[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
[agents]
clipboard_image_patch = true # Patch agents for image clipboard supportRequired for: Image paste support in Claude, Copilot, Antigravity, etc.
[daemon]
auto_start = true # Auto-start daemon on first agent runBenefits:
- Faster subsequent agent startups
- Persistent daemon across multiple runs
- Resource efficient
[daemon]
multi_paths_enabled = false
mount_paths = []Enable when: Working with multiple projects simultaneously
[daemon]
multi_paths_enabled = true
mount_paths = [
"~/Dev/Projects",
"/work/client-repos"
]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.
Configure additional packages in packages.toml:
[brew]
packages = [
"node",
"python@3.11"
]
[npm]
packages = [
"typescript",
"eslint"
]See: Packages Guide for detailed package management.
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 directoryCommon provider keys (auto-forwarded):
ANTHROPIC_API_KEYOPENAI_API_KEYANTIGRAVITY_API_KEYCNSTR_*(custom prefix)
Usage:
[claude.cc.custom]
ANTHROPIC_AUTH_TOKEN = "${CUSTOM_API_KEY}"[runtime]
engine = "auto"
[network]
mode = "permissive"[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[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[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"]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 --migrateconstruct sys doctor # Check configuration and runtime
construct sys config # Open config in editor
construct --help # Show all options