OpenClaw EnterpriseDOCSGitHub

Production controller settings

For startup configuration and precedence, see the settings reference.

Required production controller environment

The production API is internal-only by default. Operators must provision an internal Kubernetes ClusterIP Service and a default-deny ingress NetworkPolicy that allows only explicitly approved namespace and Pod selectors. Production Helm validates peer label maps. The cluster must enforce NetworkPolicies. Do not expose the listener through a NodePort, LoadBalancer, hostNetwork, or public endpoint.

The Helm charts allow DNS egress on UDP/TCP ports 53 and 5353 to their configured dns.namespace and dns.podLabels peers. Port 5353 supports OpenShift DNS backends after Service address translation. These grants cover API, worker, initialization, collector, Slack proxy, Envoy, and observability demo workloads.

The trusted-operator native admin pilot is the only documented public-ingress exception: the console host and Agent wildcard hosts route to OCC through the procedure in Deploy native admin UI access. Envoy and Agent gateway Services remain private, and OCC strips the shared OCE session cookie before forwarding to the native gateway.

Variable Required value or format Behavior
NODE_ENV Exactly production. Enables durable production controller composition.
OCC_HOST One explicit Pod interface IP address. Wildcard addresses and implicit hostnames are rejected.
OCC_PORT Decimal integer from 1 through 65535. Selects the internal listener port exposed by the operator's Service.
OCC_DATABASE_URL Explicit PostgreSQL application-role URL. Must connect to the already migrated controller database. With TLS verification, an IP host must match an IP subjectAltName.
OCC_CONFIG_PATH Absolute path to trusted Installation startup YAML. Selects Configuration, IAM, Compute, and optional account Drivers.
OCC_AUTH_SECRET Mounted high-entropy Better Auth secret with at least 32 characters. Signs and verifies session material without logging it.
OCC_AUTH_BASE_URL HTTPS DNS origin without path, query, fragment or user info; must share a non-public suffix with the Agent domain. Defines the Better Auth base URL and cookie origin.
OCC_GATEWAY_API_KEY_PATH Optional absolute path to the private gateway service-key file. API only; validates at startup and reads each operation for rotation. Requires Compute endpoint resolution.
OCC_PREPROVISIONED_NAMESPACE Optional JSON {"namespaceId","uid"} binding one operator-created namespace; set by Helm. API and worker. See preprovisioned namespace.
OCC_CHANNEL_DIRECTORY_PROXY_URL Optional HTTP(S) proxy URL with one literal IPv4 address and explicit port, or the exact Helm-managed Service URL. API only; routes Slack lookup and credential validation through an HTTP CONNECT tunnel. Invalid values fail startup.
OCC_CHANNEL_DIRECTORY_MANAGED_PROXY_HOST Optional exact Helm-managed proxy Service host. API only; the one DNS host the Slack directory Driver accepts in the proxy URL instead of an IPv4 address.
OCC_API_HTTPS_EGRESS_OPEN Optional true or false, default false; set by Helm. API only; true (open sign-in egress) makes an unanswered Slack check a retryable 503.
NODE_EXTRA_CA_CERTS Optional PEM bundle for private Gateway or IdP CAs. Node reads it at process startup. Normal leaf renewal under that CA does not require a restart; root-bundle changes do.
OCC_GATEWAY_CA_PATH Optional absolute path to the Gateway-only CA bundle; empty means no private Gateway CA. API only; Harness node enrollment reads it instead of NODE_EXTRA_CA_CERTS. Helm sets it with OIDC CA values.

For Helm, prefer slackProxy.enabled over an external api.channelDirectoryProxyUrl; the Slack Channel Driver owns both options' API-only proxy egress and the proxy's slack.com:443 requirement. With neither, the chart renders no proxy egress rule, and Slack lookup and credential validation require another approved network route.

OCC_AGENT_RUNTIME_LOGS_ENABLED (true or false, default true) switches the Agent logs routes; false makes them answer 501. The chart sets it from agentRuntimeLogs.enabled.

All browser sessions require OCC_AGENT_NATIVE_ADMIN_DOMAIN, including the bootstrap process and installations with native access disabled. In default shared-cookie mode, OCC derives the shared cookie domain from it and the HTTPS OCC_AUTH_BASE_URL. The removed OCC_AUTH_COOKIE_DOMAIN and agentNativeAdmin.sharedCookieDomain inputs are rejected.

Native browser access uses these settings:

Variable Required value or format Behavior
OCC_AGENT_NATIVE_ADMIN_ENABLED true. Enables Agent chat and administration through native browser access.
OCC_AGENT_NATIVE_ADMIN_DOMAIN Agent host suffix, such as agents.oce.example.com, without scheme, wildcard, port, or path. Required for shared cookie scope; also derives stable per-Agent browser hosts.
OCC_AGENT_NATIVE_ADMIN_SESSION_MODE shared-cookie (default) or host-only-exchange. Host-only exchange requires guarded OIDC and same-site HTTPS; see native sessions.

For changes to startup logging.level, follow the log-level procedure.

The API and worker load the same trusted startup YAML; only the API initializes the optional Backend client. Both validate Backend membership and stored ownership before accepting work. With the bundled Kubernetes Compute Driver, drivers.compute.configuration holds the KubernetesComputeDriverOptions shape. Production use of that Driver requires images.requireImmutableDigest: true, digest-pinned gateway and Agent image references, projected ServicePrincipal credentials unless a Sandbox Driver is selected, and exactly one in-cluster identity or explicitly named kubeconfig/context. The processes then verify authenticated, TLS-checked, read-only Kubernetes Namespace access before serving requests or claiming work. Installed Drivers validate their own reviewed configuration and implementation-specific prerequisites.

Agent workspace-file requests use the selected Compute Driver's private gateway endpoint; private Agent gateway routes define the hostname contract and each setting's role.

With chart-managed routing, Helm sets OCC_CHART_GATEWAY_ROUTING for the API, worker, and upgrade configuration check. It contains the chart's Gateway name, release namespace, Envoy namespace, and effective and automatic hostnames. When the bundled Kubernetes Compute Driver selects gatewayRouting, startup requires its resource identity and effective hostname to match; otherwise it reports CHART_LISTENER_MISMATCH before serving or replacing the API. This is a chart-owned consistency check, not an operator input. Correct the Installation and Helm values together; see production installation.

OCC_GATEWAY_API_KEY_PATH mounts a dedicated, high-entropy Envoy service key; never reuse the Better Auth signing secret or a model-provider credential. Missing or invalid key files fail startup; a file that becomes unavailable during rotation makes new requests unavailable. The worker needs route configuration and namespace-bound HTTPRoute permissions, but no service key or CA bundle for native file access.

The TLS lifecycle covers the generated private CA, explicit issuers, and the trust OCC loads.

See the deployment procedure for Envoy, cert-manager, native trusted-proxy configuration, and key/certificate rotation. Kubernetes gateway authentication is always trusted-proxy; private routing still requires the Installation, Helm, and service-key settings above. Unsupported Drivers and unavailable endpoints return 503 DEPENDENCY_UNAVAILABLE.

Sessions and service keys grant no rights without IAM; see denials for 401 and 403, Authentication for service-key issuance, scope, and revocation, and the deployment guide for the procedure. Normal issuance and verification require no additional settings; initial-key delivery uses the bootstrap settings below. The API refuses a shorter auth secret with AUTH_SECRET_INVALID before it constructs Drivers, opens PostgreSQL, or changes configured IAM policy. Auth-secret rotation takes effect after replacing the mounted Secret and restarting; it also invalidates every known-device cookie until each browser's next sign-in. Revoking one account's cookies needs no rotation: reset its password.

Control Plane audit

Control Plane audit is off by default. Restrict API access to approved clients and check ledger capacity before enabling it. Rejected requests can fill the ledger and block audited actions. Set Helm controlPlaneAudit.enabled: true or set OCC_CONTROL_PLANE_AUDIT_ENABLED=true on the API and worker.

External sign-in and trusted proxies

These optional variables reach only the API, never other workloads.

Variable Helm value Behavior
OCC_AUTH_RECOVERY_USER_ID auth.recoveryUserId Existing local password administrator's user ID; designates the recovery account on first activation.
OCC_AUTH_PASSWORD_SIGN_IN auth.passwordSignIn all (default, not rendered) or recovery-only: only the recovery account may use a password. Needs Google or OIDC; see recovery-only.
OCC_AUTH_TRUSTED_PROXY_CIDRS api.trustedProxy.cidrs Comma-separated IPv4 or IPv6 CIDRs, subject to the limits below. Requests whose socket peer is inside them may carry forwarded headers.
OCC_AUTH_TRUSTED_PROXY_PRESET api.trustedProxy.preset ingress-nginx (default), aws or generic. Named presets read x-forwarded-for. Set with the CIDRs.
OCC_AUTH_CLIENT_IP_HEADER api.trustedProxy.clientAddressHeader Lowercase header name, up to 64 characters, generic only. Sign-in limits key on the client address it carries from a trusted peer.

The API Deployment uses Recreate: an upgrade stops the old API Pod first, so two controllers never serve together. Activation still requires closed ingress and stopped identity writers.

Google and OIDC have API-only HTTPS egress rules. Empty provider egressCidrs allows every address except link-local 169.254.0.0/16. An explicit list replaces that default, including the exception; keep it narrow. Direct GitHub sign-in settings are rejected. See upgrade instructions.

api.trustedProxy defaults off: Forwarded, X-Forwarded-*, and X-Real-IP return 403 on API calls (sign-in still works), and the API logs authentication.forwarded-refused-warning at most once a minute: code TRUSTED_PROXY_NOT_CONFIGURED, or TRUSTED_PROXY_PEER_UNTRUSTED when the sending peer is outside api.trustedProxy.cidrs. Without a proxy, a scanner sending X-Forwarded-For can trigger TRUSTED_PROXY_NOT_CONFIGURED; ignore it and leave api.trustedProxy unset. Failed password sign-ins have per-email limits only: browsers behind a proxy share its address. With Google or OIDC, start, callback, and result key on browser cookies; start has only an active cap and the 1,000-pending-attempt cap. Startup logs authentication.sign-in-limit-warning; Helm notes and profile rendering also warn without failing. Set api.trustedProxy unless the API sees each client's address. Presets:

Trust only proxies that overwrite or append the header, and admit them through api.clients. Any IPv4-mapped IPv6 spelling counts as IPv4 (prefix 1–32). The API and chart refuse entries covering every IPv4 or IPv6 address, including /0. Mapped peers and header hops use dotted IPv4 sign-in limit keys.

Rendering rejects invalid provider configuration, shared credential Secrets, refused proxy CIDRs, or a different header for a named proxy preset. Credential and routing headers such as cookie cannot supply a client address.

Google sign-in

These optional variables also apply to the API only. Google sign-in uses the same guarded profile and OCC_AUTH_RECOVERY_USER_ID recovery user; see Google sign-in.

Variable Helm value Behavior
OCC_AUTH_GOOGLE_CLIENT_ID auth.google Secret key clientIdKey Google OAuth web client ID; determines the provider instance. Set it with the client secret and recovery user ID, or not at all.
OCC_AUTH_GOOGLE_CLIENT_SECRET auth.google Secret key clientSecretKey Google OAuth client secret, read from the dedicated auth.google.secretName Secret.
OCC_AUTH_GOOGLE_ALLOWED_DOMAINS auth.google.allowedDomains Optional comma-separated hosted domains. When set, the ID token's hd must match one and email_verified must be true. Requires the client ID.

With auth.google.enabled, the chart adds the API-only egress policy openclaw-enterprise-api-google-login-egress on TCP 443 for oauth2.googleapis.com and www.googleapis.com. Empty auth.google.egressCidrs allows any address except link-local 169.254.0.0/16; narrow it with an egress proxy. Rendering fails on incomplete Google values, a Secret shared with any other chart Secret, agentNativeAdmin.enabled with Google, an HTTP base URL, or an allowed domain that is not a DNS name.

OIDC sign-in

OIDC controller settings defines the provider URLs, client credentials, API egress and optional private CA trust. These API-only settings require the same guarded sign-in profile and recovery user and Google.

Production Installation bootstrap environment

Both environments run node scripts/bootstrap-installation.mjs after migration. NODE_ENV selects development or production; no other mode is accepted. The initializer uses the application-role database and Better Auth settings. Development consumes the OPENCLAW_DEV_* defaults and only the private service-key output path; it never writes a password file. API/worker startup requires the resulting Installation and does not create credentials.

The packaged Helm initialization Job creates the singleton Installation, human and service administrators, and initial default Namespace before starting the API or worker. Namespace provisioning completes asynchronously through the worker. Its separate migration init container receives only OCC_MIGRATION_DATABASE_URL; the bootstrap container receives the application-role OCC_DATABASE_URL, Better Auth settings, and the following bootstrap settings. The Job sets backoffLimit: 0; failed initialization requires manual repair before another attempt.

Variable Required value or format
OCC_AUTH_SECRET Same mounted Better Auth secret used by the API.
OCC_AUTH_BASE_URL Same HTTPS DNS origin as the API.
OCC_AGENT_NATIVE_ADMIN_DOMAIN Same Agent DNS suffix as the API; required for derived shared session cookies.
OCC_BOOTSTRAP_ADMIN_EMAIL Email address for the first administrator account.
OCC_BOOTSTRAP_PASSWORD_FILE New file path on protected operator-owned storage for the generated password.
OCC_BOOTSTRAP_INSTALLATION_NAME Installation display name; it must follow the API Name rule (INSTALLATION_NAME_INVALID).
OCC_BOOTSTRAP_SERVICE_KEY_FILE New private absolute JSON path; on fresh production bootstrap, a distinct sibling of the password file.

Repeated bootstrap preserves the existing Installation only when the exact administrator account and IAM identity still match; a mismatch fails closed. Existing Namespaces and their configuration remain unchanged; no initial Namespace is backfilled or recreated. For a read-only check of a populated candidate database, set the usual production database, authentication and administrator settings, then run node scripts/bootstrap-installation.mjs --verify-existing "$incumbent_installation_id". Obtain the ID independently from the authenticated incumbent Installation before selecting the candidate database. The command checks the singleton ID and the ordinary bootstrap administrator account, Principal and direct role binding in one read-only PostgreSQL snapshot. On success it prints installation.bootstrap-verified and the matched ID. An empty database, a different ID or a failed administrator check returns a nonzero exit. This mode requires no bootstrap output paths and creates no credentials. Check migration history separately with scripts/migrate-production.mjs --require-current.

On fresh bootstrap, both files are created exclusively with mode 0600; their parent directory must be private and neither destination may already exist. Helm sets the key path from bootstrap.password.mountPath and bootstrap.serviceKey.fileName (default initial-admin-service-key.json). The key filename must be a simple basename distinct from bootstrap.password.fileName. bootstrap.password.claimName must follow Kubernetes' DNS-subdomain name rule: 253 characters total, the same rule prepare-bootstrap-volume applies to --claim. Both use that existing PVC. Reruns do not inspect, replace, or regenerate output; see recovery.

Production operational logging collection

logging.collector configures the bundled Helm Collector; enabled defaults to false. For enablement, existing-Collector reuse, Secret creation, networking, and delivery checks, use Configure platform observability.

When enabled, the chart requires a digest-pinned image, an exact exporter destination (IPv4 /32 or paired namespace/Pod selectors), a TCP port, and nonempty configuration and environment Secret names that differ from each other and from every other Secret the chart reads or cert-manager writes for an enabled feature. The named Secrets must be in the control-plane namespace:

Relevant Helm values:

yaml
logging:  collector:    enabled: true    image: docker.io/otel/opentelemetry-collector-contrib:0.159.0@sha256:1f2c54a30e713fac6b3ae77a1ec84010c2007e29ced8ec666214fc2f6739c1cc    configSecretName: occ-otel-collector-config    envSecretName: occ-otel-collector-exporter    exporter:      cidr: 203.0.113.10/32      port: 443    state:      sizeLimit: 128Mi

See chart defaults for resources, state.sizeLimit, and tmp.sizeLimit. The security reference owns the credential, runtime-export, and workload isolation boundaries.

The chart rejects obvious malformed quantities such as foo, 10MiB, and 1K in top-level resources, slackProxy.resources, Collector resources, and Collector volume size limits, with an error naming the setting. It also rejects a value with a leading - (negative) and resource names Kubernetes refuses. Kubernetes remains responsible for complete quantity validation; exponent ranges, numeric parsing edge cases and a request above its limit are not checked during rendering. A successful render does not prove API acceptance. Setting a resource map or Collector size limit to null clears its chart default; a null size limit leaves that volume unlimited.

Private telemetry defaults

metrics.enabled defaults to true, with API and worker listeners on their Pod IP at port 9464. Both metrics.scraperNamespaceLabels and metrics.scraperPodLabels default to empty: no metrics ingress is granted until both are set. Partial selectors and invalid or API-colliding ports fail rendering. See scraping and discovery.

For an in-cluster log receiver, set both logging.collector.exporter.namespaceLabels and podLabels, set decimal port (1–65535, no leading zeros), and leave cidr empty. This alternative cannot be combined with a CIDR. Collector metrics use the same paired selector contract under logging.collector.metrics, on fixed port 8888; metrics ingress is opt-in. The chart grants only the selected peer and port. Other NetworkPolicies remain additive, so review them when assessing effective access.

Search documentation