Environment Variables
Cloud Harness MCP is configured via environment variables supplied to the stateless API, the Runner, and the Cloudflare Worker Gateway.
Copy .env.example to .env and replace all change-me placeholder secrets before starting services.
Configuration Table
| Variable | Default / Example | Required / Mode | Description & Purpose |
|---|---|---|---|
MCP_BEARER_TOKEN | change-me-at-least-32-random-characters | Required | Copy to .env and replace every change-me value. Never commit real secrets. |
RUNNER_TOKEN | change-me-independent-runner-token | Required | Required configuration. |
OWNER_ID | owner | Required | Required configuration. |
AUTH_MODE | owner-bearer | Required | owner-bearer (default) or cloudflare-access. In Access mode, remove MCP_BEARER_TOKEN. |
CLOUDFLARE_ACCESS_ISSUER | https://your-team.cloudflareaccess.com | Optional | Optional configuration. |
CLOUDFLARE_ACCESS_AUDIENCE | — | Optional | Optional configuration. |
CLOUDFLARE_ACCESS_JWKS_URL | https://your-team.cloudflareaccess.com/cdn-cgi/access/certs | Optional | Optional configuration. |
API_KEY_AUTH_ENABLED | false | Optional | Optional managed API-key lane. Enable all four together only in cloudflare-access mode. The gateway audience belongs to a separate Access application scoped exactly to /mcp-api-key. |
API_KEY_GATEWAY_ACCESS_AUDIENCE | — | Optional | Optional configuration. |
API_KEY_GATEWAY_SERVICE_SUBJECT | cf-service:base64url-cloudflare-service-token-client-id | Optional | Optional configuration. |
API_KEY_GATEWAY_PUBLIC_URL | https://api.harness.zuey.me/mcp | Optional | Optional configuration. |
ACCESS_LEGACY_OWNER_ID | owner | Optional | Worker-only secrets CF_ACCESS_CLIENT_ID and CF_ACCESS_CLIENT_SECRET are configured with Wrangler, never here. Exact one-time legacy owner binding for the first Access rollout: |
ACCESS_LEGACY_ISSUER | https://your-team.cloudflareaccess.com | Optional | Optional configuration. |
ACCESS_LEGACY_SUBJECT | — | Optional | Optional configuration. |
ACCESS_PRINCIPAL_RELINKS | [] | Optional | Optional audited subject-rotation mappings, supplied as strict JSON: |
API_PUBLIC_HOSTS | localhost,127.0.0.1,cloud-harness-mcp.46-250-239-227.sslip.io | Required | Required configuration. |
API_ALLOWED_ORIGINS | https://cloud-harness-mcp.46-250-239-227.sslip.io | Required | Required configuration. |
API_PORT | 3000 | Required | Required configuration. |
RUNNER_PORT | 3001 | Required | Required configuration. |
RUNNER_URL | http://runner:3001 | Required | Required configuration. |
MCP_GATEWAY_TIMEOUT_MS | 30000 | Optional | MCP gateway (/mcp-gateway) downstream connection bounds and safe defaults. |
MCP_GATEWAY_MAX_RESPONSE_BYTES | 262144 | Optional | Optional configuration. |
MCP_GATEWAY_MAX_TOOLS_PER_SERVER | 500 | Optional | Optional configuration. |
MCP_GATEWAY_MAX_SCHEMA_BYTES | 65536 | Optional | Optional configuration. |
MCP_GATEWAY_MAX_CATALOG_BYTES | 2097152 | Optional | Optional configuration. |
MCP_GATEWAY_MAX_TRACE_ROWS | 20000 | Optional | Optional configuration. |
MCP_GATEWAY_MAX_CONNECTIONS | 32 | Optional | Optional configuration. |
MCP_GATEWAY_ALLOW_PRIVATE_ENDPOINTS | false | Optional | Localhost, loopback, link-local, metadata, and private MCP endpoints are rejected unless the private-endpoint opt-in is enabled. Cleartext http endpoints additionally require both the insecure-http and private-endpoint opt-ins. Both opt-ins are refused in cloudflare-access mode; use https public endpoints there. |
MCP_GATEWAY_ALLOW_INSECURE_HTTP | false | Optional | Optional configuration. |
MODEL_GATEWAY_SESSION_HEADER | x-opencode-session | Optional | Model gateway provider override. When set, the gateway sends this one upstream header, filled with the calling agent id, to OpenAI-compatible providers that require a conversation identifier (for example x-opencode-session for OpenCode Go). Unset sends no such header. The value must be a lowercase header name the gateway does not set itself. |
JOBS_ROOT | /var/lib/cloud-harness/jobs | Required | Required configuration. |
STATE_DB | /var/lib/cloud-harness/state/cloud-harness.db | Required | Required configuration. |
ARTIFACT_ROOT | /var/lib/cloud-harness/artifacts | Required | Required configuration. |
MAX_ARTIFACT_BYTES | 16777216 | Required | Required configuration. |
MAX_PRINCIPAL_ARTIFACT_BYTES | 134217728 | Required | Required configuration. |
ARTIFACT_RETENTION_SECONDS | 86400 | Required | Required configuration. |
REPO_CACHE_ROOT | /var/lib/cloud-harness/cache/repos | Required | Required configuration. |
ENABLE_REPO_CACHE | false | Optional | Optional configuration. |
TOOLKIT_CACHE_ROOT | /var/lib/cloud-harness/cache/toolkits | Required | Required configuration. |
ENABLE_TOOLKIT_CACHE | true | Optional | Optional configuration. |
TOOLKIT_NETWORK_POLICY | cache-only | Optional | Optional configuration. |
BUILTIN_SKILLS_ROOT | /var/lib/cloud-harness/skills | Optional | Operator-provided agent skills: the single source for the built-in skills tier. Set to a host directory (absolute) and it is mounted read-only into every executor at /opt/cloud-harness/skills, the worker's highest-precedence tier, and rescanned by the runner from this same directory to attribute that partition. Content is operator-owned: the harness never writes to it and records its provenance as built-in. The mount target stays authoritative on the executor side. This name is reserved, so a workspace environment can never shadow it. Replaces the former CH_BUILTIN_SKILLS_ROOT override: rename that variable to this one, or move the catalog to the mount target. Leave unset to keep the tier empty. |
AGENTKIT_REGISTRY_URL | https://agentkit.best | Optional | Licensed AgentKit kits (toolkits: [{ kind: "agentkit", kitId: "engineer" }]). |
AGENTKIT_REGISTRY_CREDENTIAL_SECRET | AGENTKIT_REGISTRY_TOKEN | Optional | The secret name that holds this principal's AgentKit licence token. The token must start with ak_dev_ or ak_cli_ and MUST be created with purpose=provisioning, never runtime: runtime secrets are injected into executor environments. It is resolved from the dashboard secret store; without it the agentkit toolkit kind fails closed. |
AGENTKIT_REGISTRY_KEY_ID | agentkit-registry-2026 | Optional | Ed25519 registry signing key id. Required before the agentkit toolkit kind is available. |
AGENTKIT_REGISTRY_PUBLIC_KEY | <PEM or base64 SPKI DER> | Optional | Ed25519 registry signing public key (PEM or base64 SPKI DER). Required before the agentkit toolkit kind is available; an unverified manifest is refused. |
EXECUTOR_IMAGE | cloud-harness-executor:local | Required | Required configuration. |
NETWORK_GUARD_IMAGE | cloud-harness-network-guard:local | Required | Required configuration. |
ALLOWED_GIT_HOSTS | github.com | Required | Required configuration. |
WORKSPACE_NETWORK_PROFILE | dependency-access | Required | Executor egress for newly opened workspaces. dependency-access (default) permits public DNS and TCP 80/443 so the GitHub API and the bundled gh CLI work; network-none blocks all egress. dependency-access requires a Linux host firewall provisioned via deploy/scripts/setup-dependency-firewall.sh and is refused, never silently downgraded, when that firewall is not attested. The dashboard Settings page overrides this value for future workspaces without a redeploy. |
DEPENDENCY_DNS_RESOLVERS | 8.8.8.8,1.1.1.1 | Optional | Optional configuration. |
DEPENDENCY_BRIDGE_SUBNET | 172.30.240.0/24 | Optional | Optional configuration. |
DEPENDENCY_BRIDGE_INTERFACE | chm-egress0 | Optional | Optional configuration. |
DEPENDENCY_NETWORK_NAME | cloud-harness-dependency-access | Optional | Optional configuration. |
WORKSPACE_WALL_TTL_SECONDS | 900 | Required | Required configuration. |
WORKSPACE_IDLE_TTL_SECONDS | 300 | Required | Required configuration. |
MAX_ACTIVE_WORKSPACES_PER_OWNER | 3 | Required | Concurrent active workspaces allowed per principal. Defaults to 3 when unset; set 1 to restore single-workspace behaviour. Each counted workspace can use up to 1 GiB of container memory, one CPU, and 256 pids, so size host memory for this limit times the expected simultaneous builds. |
GITHUB_APP_ID | — | Optional | Optional GitHub App repository access; required fields depend on AUTH_MODE: |
GITHUB_APP_INSTALLATION_ID | — | Optional | Required in owner-bearer mode; omit in Access mode, where each principal binds an installation: |
GITHUB_APP_SLUG | — | Optional | Required in Access mode for the installation redirect: |
GH_TOKEN | — | Optional | Optional operator-wide GitHub fallback credential, read from GH_TOKEN (preferred) then GITHUB_TOKEN, each also accepting a _FILE form. Used only by the runner, only when no GitHub App repository token can be minted, and only in owner-bearer mode: an operator-wide credential must never authorize a different principal. In cloudflare-access mode, create a per-principal global runtime secret named GH_TOKEN or GITHUB_TOKEN in the dashboard instead. This credential authenticates harness-side GitHub operations only and is never placed in executor environments. To authenticate the bundled gh CLI inside a workspace, create a global runtime secret named GH_TOKEN or GITHUB_TOKEN. |
GITHUB_TOKEN | — | Optional | Optional configuration. |
GITHUB_APP_PRIVATE_KEY_FILE | /run/cloud-harness-secrets/github-app-private-key.pem | Optional | Production host file: /etc/cloud-harness-mcp/github-app-private-key.pem |
SECRET_KEYRING_FILE | /run/cloud-harness-secrets/secret-keyring.json | Optional | Versioned AES-256-GCM keyring JSON. Prefer the runner-only file form. |
Security Guidelines
- Never commit
.envfiles or tokens into version control. - Runner secrets isolation:
RUNNER_TOKENandSECRET_KEYRING_FILEare passed only to the Runner container, never to the API or workspace executors. - Managed OAuth vs Bearer: When
AUTH_MODE=cloudflare-access, removeMCP_BEARER_TOKENand configureCLOUDFLARE_ACCESS_*variables instead. - Executor Isolation: Executors never inherit host environment variables or control plane tokens.