> ## Documentation Index
> Fetch the complete documentation index at: https://docs.acornops.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration reference

> Detailed platform settings for hosts, auth, model access, runtime policy, egress, and delivery

Configuration is split between public host settings, Kubernetes or Compose deployment values, and secret values. Keep secrets out of source control and inject them through the platform secret mechanism for your deployment target.

## Target and assistant settings

The platform chart keeps target connectivity, assistant behavior, and target-specific installation settings separate:

| Helm section                  | Configures                                                                                        | Does not configure                                |
| ----------------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------------------------- |
| `agentGateway`                | Shared control-plane WebSocket routing and snapshot policy for AgentK and AgentV                  | Either target-side daemon                         |
| `assistantRuntime`            | AI assistant budgets, limits, timeouts, and write-approval defaults                               | AgentK or AgentV collection behavior              |
| `targetAgents.agentk.helm`    | AgentK Helm install commands generated for Kubernetes targets                                     | AgentV, which uses a systemd installation archive |
| `targetAgents.agentv.systemd` | Exact AgentV systemd release version and immutable release base URL used by generated VM commands | Host package installation or Node.js installation |
| `builtinTargetMcp`            | The internal MCP identity used to expose both AgentK and AgentV tools                             | A standalone MCP deployment                       |

There is intentionally no generic `agent` section and no AgentV Helm section. Each name identifies one responsibility and one target scope.

Production must set `targetAgents.agentv.systemd.version` to an exact semantic version and `releaseBaseUrl` to HTTPS. The public default is `https://github.com/acornops/agentv/releases/download`. An internal mirror must expose the identical `v<version>/install-agentv.sh`, archive, and checksum layout; mutable labels such as `latest` are rejected.

## Public hosts

| Setting                | Example self-host value                      | Used by                                                                            |
| ---------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------- |
| Platform public URL    | `https://api.example.com`                    | Primary API route examples and agent install commands                              |
| Management console URL | `https://console.example.com`                | Browser app origin, same-origin `/api` calls, and default OIDC callback derivation |
| Public docs URL        | `https://docs.acornops.dev`                  | Documentation links                                                                |
| Agent WebSocket URL    | `wss://api.example.com/api/v1/agent/connect` | Target agent connections                                                           |

Replace `example.com` with domains you control. The public demo uses `https://console.demo.acornops.dev` and `https://api.demo.acornops.dev`. The platform route and the management console route are separate, but both default deployment paths proxy `/api` to the control plane. The management console uses its own origin for browser session flows so cookies stay same-origin.

For Kubernetes, configure these with `platform.publicUrl`, `platform.consoleUrl`, `exposure.ingress.apiHost`, and `exposure.ingress.consoleHost`. For VM Compose, configure `CONTROL_PLANE_BASE_URL`, `API_HOST`, `MANAGEMENT_CONSOLE_HOST`, `MANAGEMENT_CONSOLE_UPSTREAM`, and `CORS_ORIGIN`. Keep `TRUST_PROXY=1` when TLS and host headers are handled by the edge proxy.

## Required secret keys

The Kubernetes chart defaults to an existing Secret named `acornops-platform-secrets`. These keys are required for the central platform:

| Key                                   | Purpose                                                                            |
| ------------------------------------- | ---------------------------------------------------------------------------------- |
| `CONTROL_PLANE_DATABASE_URL`          | Control-plane Postgres connection                                                  |
| `CONTROL_PLANE_REDIS_URL`             | Control-plane Redis connection                                                     |
| `OIDC_CLIENT_SECRET`                  | Browser sign-in client secret                                                      |
| `CSRF_SECRET`                         | Browser CSRF token signing secret                                                  |
| `GATEWAY_SIGNING_PRIVATE_KEY_PEM_B64` | Stable control-plane signing key for run-scoped gateway JWTs                       |
| `ORCH_SERVICE_TOKEN`                  | Execution-engine callbacks into control plane                                      |
| `EXTERNAL_INTEGRATION_CLIENTS_JSON`   | Installed external integration client descriptors with SHA-256 bearer-token hashes |
| `WEBHOOK_SECRET_ENCRYPTION_KEY`       | Encryption for webhook signing secrets                                             |
| `EXECUTION_ENGINE_REDIS_URL`          | Execution-engine Redis connection                                                  |
| `EXECUTION_ENGINE_DISPATCH_TOKEN`     | Control-plane dispatch auth into execution engine                                  |
| `LLM_GATEWAY_DATABASE_URL`            | LLM-gateway Postgres connection                                                    |
| `LLM_GATEWAY_REDIS_URL`               | LLM-gateway Redis connection                                                       |
| `LLM_GATEWAY_ADMIN_TOKEN`             | Control-plane admin auth into LLM gateway                                          |
| `SECRETS_KEK_BASE64`                  | LLM-gateway database secret encryption key                                         |

Optional secret-backend keys include:

| Key           | Purpose                                           |
| ------------- | ------------------------------------------------- |
| `VAULT_TOKEN` | Vault secret-backend access when Vault is enabled |

Generate unique values for every internal token and encryption key per environment.

## Provider credentials

Provider API keys are not configured through `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, or `GEMINI_API_KEY` deployment variables. Configure them as write-only credentials:

* per workspace in **Workspace Settings → AI**, or
* as an optional platform default in **Platform Settings → AI** in the platform admin console.

A workspace credential takes precedence over a platform default. APIs and consoles expose only configured status and the effective source (`workspace`, `platform_default`, or `none`); they never return credential plaintext.

## Provider routing and API surfaces

Platform operators can route provider traffic through API-compatible endpoints without changing workspace credentials:

| Provider  | Kubernetes value                                   | VM Compose variable               |
| --------- | -------------------------------------------------- | --------------------------------- |
| OpenAI    | `components.llmGateway.providerBaseUrls.openai`    | `LLM_PROVIDER_OPENAI_BASE_URL`    |
| Anthropic | `components.llmGateway.providerBaseUrls.anthropic` | `LLM_PROVIDER_ANTHROPIC_BASE_URL` |
| Gemini    | `components.llmGateway.providerBaseUrls.gemini`    | `LLM_PROVIDER_GEMINI_BASE_URL`    |

An empty value uses the provider SDK's vendor endpoint. Provider SDK-specific environment variables are not part of the AcornOps configuration contract.

OpenAI uses the Responses API by default. Select an API-compatible Chat Completions endpoint explicitly with:

* `components.llmGateway.openaiApiSurface=chat_completions` on Kubernetes, or
* `LLM_PROVIDER_OPENAI_API_SURFACE=chat_completions` with VM Compose.

The gateway never probes or falls back between surfaces. Chat Completions supports normalized text and custom function calls, but it does not support AcornOps provider-native tools or reasoning summaries. Configured [Web Search](/use/web-search) remains visible but unavailable for ordinary target runs. Restore `responses` to roll back.

## Git skill import hosts

Users import a skill by pasting one repository, folder, or `SKILL.md` URL.
Configure the accepted GitHub and GitLab hosts with `gitImports.hosts`. The
chart enables GitHub.com and GitLab.com by default. Setting this array replaces
those defaults, so include every host that users should access.

```yaml theme={null}
gitImports:
  hosts:
    - provider: github
      webBaseUrl: https://github.example.com
      apiBaseUrl: https://github.example.com/api/v3
    - provider: gitlab
      webBaseUrl: https://gitlab.example.com
      apiBaseUrl: https://gitlab.example.com/api/v4
```

The control plane matches the pasted URL against this allowlist, infers the
provider, ref, and subpath, and stores a pinned Markdown snapshot. Requests are
anonymous, so repositories must be publicly readable from the control-plane
pod. For an internal host, add the destination to
`networkPolicies.extraEgress.controlPlane` using its stable IP or CIDR and HTTPS
port. Add organization CA trust when the host does not use a public certificate
chain.

## Automation runtime

| Environment variable                           | Helm value                                         | Default             | Purpose                                                               |
| ---------------------------------------------- | -------------------------------------------------- | ------------------- | --------------------------------------------------------------------- |
| `AUTOMATION_RUNTIME_MODE`                      | `automation.runtimeMode`                           | `off` in production | Select `off`, `shadow`, `canary`, or `on`.                            |
| `AUTOMATION_CANARY_WORKSPACE_IDS`              | `automation.canaryWorkspaceIds`                    | empty               | Comma-separated workspace IDs eligible in `canary` mode.              |
| `AUTOMATION_WORKER_INTERVAL_MS`                | `automation.workerIntervalMs`                      | `1000`              | Poll interval for durable schedules and dispatch intent.              |
| `ASSISTANT_WRITE_CONFIRMATION_TIMEOUT_SECONDS` | `assistantRuntime.writeConfirmationTimeoutSeconds` | `900`               | Expires Workflow-run and target-run write approvals after 15 minutes. |

See [Automation runtime](/deploy/automation-runtime) for rollout order and operational diagnostics.

`EXTERNAL_INTEGRATION_CLIENTS_JSON` stores descriptors such as client id,
provider, display name, enabled flag, lowercase SHA-256 token hash, and optional
`allowedCapabilities`. Generate raw bearer tokens out of band and store only
their hashes in this Secret value. Raw external integration client tokens do not
authorize general control-plane API calls; they are accepted only by the external
integration account-link, linked-user bot, and external webhook route
connect/status endpoints.

`allowedCapabilities` is an operator-side ceiling for that registered client. If
you omit it, AcornOps uses the default external integration ceiling:
`read_workspace_data`, `create_sessions`, and `create_read_only_runs`. Users
still approve per-workspace grants when they link the external account. Add
`create_read_write_runs` only when the client may request write-capable
troubleshooting runs or active read-write/approval-gated Workflows; keep
`read_workspace_data` and `create_sessions` because run creation depends on
them. The same linked integration and client may decide approvals only for
troubleshooting runs or Workflow executions it originated, and only after an
explicit linked-user confirmation. Browser-created, other-link/client,
scheduled, and system-triggered approvals remain denied.

## Internal transport TLS

Kubernetes platform installs default to plaintext HTTP between internal platform
services. To enable internal HTTPS/mTLS, set `internalTransport.tls.enabled=true`
and provide Kubernetes Secret names under `internalTransport.tls.ca.secretName`
and `internalTransport.tls.certificates.*.secretName`.

The chart accepts only Secret names and key names. It does not accept raw PEM
certificate or private key values. Public ingress stays on the control-plane HTTP
service port; the chart adds a separate internal mTLS listener for callbacks,
JWKS, and the built-in MCP bridge. Kubelet probes use dedicated health ports for
services that require mTLS for application traffic.

The built-in MCP bridge uses the same run-scoped JWT authorization as other
execution-time LLM gateway calls. There is no separate built-in MCP service
token to configure.

## OIDC

The control plane owns OIDC login and callback handling:

* Login entrypoint: `GET /api/v1/auth/oidc/login?return_to=<management-console-url>`
* Callback entrypoint: `GET /api/v1/auth/oidc/callback`
* Logout handoff: `GET /api/v1/auth/oidc/logout/start`
* Post-logout callback: `GET /api/v1/auth/oidc/logout/callback`

For Kubernetes and VM Compose settings that derive the redirect URI from your console URL, register this redirect URI with your provider:

```text theme={null}
https://console.example.com/api/v1/auth/oidc/callback
```

That URL is still served by the control plane through the console host's `/api` proxy. If you override `userAccess.oidc.redirectUri` or `OIDC_REDIRECT_URI`, register the exact override value instead. Registering only `https://api.example.com/api/v1/auth/oidc/callback` will fail unless your deployment is configured to use that URL as the OIDC redirect URI.

Register this exact post-logout redirect URI when your provider supports
RP-initiated logout:

```text theme={null}
https://console.example.com/api/v1/auth/oidc/logout/callback
```

Common OIDC settings:

| Setting                       | Notes                                                                                                                                                                            |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Issuer URL                    | Provider issuer used for discovery and token validation                                                                                                                          |
| Public issuer URL             | Optional override when internal and public issuer URLs differ                                                                                                                    |
| Client ID                     | OIDC client configured for AcornOps                                                                                                                                              |
| Client secret                 | Referenced by `userAccess.oidc.clientSecret.existingSecret` and `key`; it is not required when OIDC is disabled                                                                  |
| Scopes                        | Defaults to `openid profile email`                                                                                                                                               |
| Token endpoint auth method    | Defaults to client-secret based auth                                                                                                                                             |
| Admission policy              | `userAccess.oidc.admission` or `OIDC_ADMISSION_POLICY_JSON`; an empty policy allows any authenticated OIDC identity                                                              |
| Explicit prelinks             | `OIDC_PRELINKED_IDENTITIES_JSON` supplied through the control-plane environment; each mapping requires the exact provider subject, email, display name, and verification boolean |
| End-session endpoint override | Public browser-facing logout endpoint for split internal/public provider routing                                                                                                 |
| Post-logout redirect URI      | Exact callback registered with the provider                                                                                                                                      |

`OIDC_PROVIDER_NAME` is a stable persisted identity namespace, not only a
display label. Do not reuse the same value for an unrelated issuer. AcornOps
matches returning identities by that provider namespace and the verified
`sub` claim; it never attaches a previously unseen subject to an existing
account based only on an email collision. Such users must sign in with the
account's existing method and explicitly connect SSO when that capability is
available.

Controlled fixtures and bootstrap deployments can explicitly prelink accounts
with `OIDC_PRELINKED_IDENTITIES_JSON`. Reconciliation runs atomically before
the server accepts traffic, is idempotent for the same mapping, and fails
startup if an email or provider subject is already mapped differently. It does
not weaken admission: every subsequent login is still evaluated against the
configured admission policy. Never derive this configuration from email alone;
obtain the exact `sub` from the provider's administrative data.

The former `OIDC_REQUIRE_VERIFIED_EMAIL` setting has been removed and is
rejected at startup. Express that requirement with
`requireVerifiedEmail: true` in the admission policy.

Admission rules are combined with AND semantics. You can require a literal
`email_verified=true`, allow exact email domains, and require claims using
`exists`, `equals`, `contains`, or `intersects`. Claim paths are arrays, which
keeps nested and namespaced claims unambiguous:

```yaml theme={null}
userAccess:
  oidc:
    enabled: true
    clientSecret:
      existingSecret: acornops-platform-secrets
      key: OIDC_CLIENT_SECRET
    admission:
      requireVerifiedEmail: true
      allowedEmailDomains: [example.com]
      requiredClaims:
        - path: [groups]
          operator: intersects
          values: [acornops-users, platform-sre]
    logout:
      endSessionEndpointOverride: https://identity.example.com/realms/acornops/protocol/openid-connect/logout
      postLogoutRedirectUri: https://console.example.com/api/v1/auth/oidc/logout/callback
```

The control plane compares admission claims from the verified ID token and
subject-bound UserInfo response without type coercion. A conflicting value
fails closed. Admission runs before account creation, identity linking, or
session creation.

Logout deletes the current AcornOps session before redirecting to the provider.
Other AcornOps browser sessions are not revoked. The provider may still treat
RP-initiated logout as termination of its broader SSO session; that behavior is
provider-dependent.
If the provider has no usable end-session endpoint, AcornOps completes local
logout and warns that the provider SSO session may remain active. In that case,
the next login can authenticate without showing the provider login screen.

## Password auth

Password login is enabled by default alongside OIDC, password reset is enabled by default for password-backed accounts, and self-service signup is disabled in production deployment configs. The management console reads `GET /api/v1/auth/config` and shows only the enabled login methods.

Operators can:

* disable password login with `userAccess.password.enabled=false` or `PASSWORD_AUTH_ENABLED=false`,
* disable password reset with `userAccess.password.resetEnabled=false` or `PASSWORD_RESET_ENABLED=false`,
* allow `password` in `platformSettings.userSignInMethods.allowedMethods` and
  `defaultMethods`, then manage the effective sign-in methods from the audited
  Platform Admin settings page. Self-service signup becomes available only
  when the password email-verification prerequisites are also ready.

Only enable self-service signup in private deployments where account creation has been reviewed.

Password reset and self-service password signup use AcornOps auth email delivery. Configure SMTP delivery with `email.deliveryMode=smtp`, `email.from`, `email.publicBaseUrl`, and `email.smtp.*` Helm values, backed by `SMTP_USERNAME` and `SMTP_PASSWORD` in the platform Secret.

Password reset defaults:

* `PASSWORD_RESET_TOKEN_TTL_SECONDS`: `3600`
* `PASSWORD_RESET_REQUEST_WINDOW_SECONDS`: `300`

Development environments may use `EMAIL_DELIVERY_MODE=log`. Production rejects log delivery unless `EMAIL_DELIVERY_ALLOW_LOG_IN_PRODUCTION=true` is set explicitly.

When self-service signup is enabled, email verification is required by default. Only private deployments should disable verification with `PASSWORD_SIGNUP_ALLOW_UNVERIFIED_EMAIL=true`.

Development deployments may expose a dev-login endpoint. Do not enable dev-login in production.

## Browser sessions

Browser sessions have both an absolute max age and a sliding idle timeout. `SESSION_MAX_AGE_SECONDS` defaults to `604800` and controls the absolute session lifetime. `SESSION_IDLE_TIMEOUT_SECONDS` defaults to `86400` and refreshes on active authenticated requests until the absolute max age is reached.

`SESSION_TTL_SECONDS` is still accepted as a legacy fallback for `SESSION_MAX_AGE_SECONDS` when the newer variable is unset. Keep `SESSION_IDLE_TIMEOUT_SECONDS` less than or equal to the effective max age.

## LLM providers and run limits

The control plane sets default model policy and runtime budgets for runs:

| Setting area        | Examples                                                            |
| ------------------- | ------------------------------------------------------------------- |
| Providers           | `openai`, `anthropic`, `gemini`                                     |
| Models              | Provider-specific allowed model list                                |
| Runtime limits      | max runtime, max steps, max tool calls, duplicate tool-call limit   |
| Output limits       | max context tokens, max output tokens, budget cents                 |
| Sampling            | default temperature                                                 |
| Reasoning summaries | deployment enablement, allowed summary modes, allowed effort levels |

The LLM gateway enforces the run-scoped JWT minted by the control plane. It should not infer provider, model, or tool permissions from request body fields alone.

Reasoning summaries are workspace opt-in and off by default for each workspace. When enabled in AI Settings, OpenAI, Anthropic, and Gemini may stream short provider-generated summaries while a response is being generated. AcornOps displays summaries only; it does not request or expose raw chain-of-thought, encrypted reasoning items, thinking signatures, or provider-internal reasoning state.

Operators can set the deployment policy ceiling with:

| Kubernetes value                  | Compose variable                      | Default                     |
| --------------------------------- | ------------------------------------- | --------------------------- |
| `ai.reasoningSummariesEnabled`    | `LLM_REASONING_SUMMARIES_ENABLED`     | `true`                      |
| `ai.allowedReasoningSummaryModes` | `LLM_ALLOWED_REASONING_SUMMARY_MODES` | `off,auto,concise,detailed` |
| `ai.allowedReasoningEfforts`      | `LLM_ALLOWED_REASONING_EFFORTS`       | `off,low,medium,high`       |

Summaries are saved in run event history when enabled, so users who reconnect or review a completed run see the same summary trail. Provider and model support varies, and summaries can increase provider latency or billable reasoning/output tokens.

## Gateway auth readiness and limits

The LLM gateway validates run-scoped JWTs against the control plane's JWKS endpoint. Keep JWKS readiness required in production so gateway pods do not accept runtime traffic before they have a fresh signing-key view.

| Kubernetes value                                              | VM Compose variable                    | Default   | Purpose                                                       |
| ------------------------------------------------------------- | -------------------------------------- | --------- | ------------------------------------------------------------- |
| `components.llmGateway.maxRequestBodyBytes`                   | `LLM_GATEWAY_MAX_REQUEST_BODY_BYTES`   | `1000000` | Maximum request body size accepted by the LLM gateway.        |
| `components.llmGateway.auth.jwksCacheTtlSeconds`              | `JWKS_CACHE_TTL_SECONDS`               | `300`     | JWKS cache TTL.                                               |
| `components.llmGateway.auth.jwksReadinessMaxStalenessSeconds` | `JWKS_READINESS_MAX_STALENESS_SECONDS` | `900`     | Maximum JWKS age allowed for readiness.                       |
| `components.llmGateway.auth.requireJwksReadiness`             | `REQUIRE_JWKS_READINESS`               | `true`    | Requires a fresh JWKS view before gateway readiness succeeds. |
| `components.executionEngine.maxRequestBodyBytes`              | `MAX_REQUEST_BODY_BYTES`               | `1000000` | Maximum request body size accepted by the execution engine.   |

## Write confirmations

Write-capable AgentK and AgentV tools require confirmation by default. AgentV's only built-in write is the separately gated `restart_service`. The deployment default is controlled by:

| Setting                                                | Default | Purpose                                                                  |
| ------------------------------------------------------ | ------- | ------------------------------------------------------------------------ |
| `ASSISTANT_WRITE_CONFIRMATION_REQUIRED`                | `true`  | Requires approval before write-capable tools execute.                    |
| `ASSISTANT_WRITE_CONFIRMATION_TIMEOUT_SECONDS`         | `900`   | Bounds how long a paused run waits for a decision.                       |
| `TARGET_CHAT_RECENT_ACTIVITY_WINDOW_SECONDS`           | `300`   | Controls the recent activity window used for target chat warnings.       |
| `assistantRuntime.writeConfirmationRequired`           | `true`  | Helm value rendered into `ASSISTANT_WRITE_CONFIRMATION_REQUIRED`.        |
| `assistantRuntime.writeConfirmationTimeoutSeconds`     | `900`   | Helm value rendered into `ASSISTANT_WRITE_CONFIRMATION_TIMEOUT_SECONDS`. |
| `components.controlPlane.recentActivity.windowSeconds` | `300`   | Helm value rendered into `TARGET_CHAT_RECENT_ACTIVITY_WINDOW_SECONDS`.   |

Clusters can inherit the deployment default or set a per-cluster override. Required confirmations are enforced by the backend runtime before tool execution. Browser chat cards and bot surfaces only submit explicit approve or reject decisions.

In the management console, per-cluster write confirmation policy is managed from Cluster Settings.

## Workflow and evidence retention

Workflow execution duration, generated-document retention, and complete tool-result artifact limits are deployment-wide. Workflow definitions cannot override them.

| Helm value                                                  | Rendered environment variable         | Default   | Purpose                                                                         |
| ----------------------------------------------------------- | ------------------------------------- | --------- | ------------------------------------------------------------------------------- |
| `agent.runtime.maxRuntimeMs`                                | `AGENT_MAX_RUNTIME_MS`                | `600000`  | Maximum execution duration for Workflow and target-chat runs.                   |
| `components.controlPlane.reportArtifacts.maxRetentionDays`  | `GENERATED_DOCUMENT_RETENTION_DAYS`   | `30`      | Retention period for generated documents. Accepts `1` through `365`.            |
| `components.controlPlane.toolResultArtifacts.retentionDays` | `TOOL_RESULT_ARTIFACT_RETENTION_DAYS` | `7`       | Retention for eligible complete redacted tool results. Accepts `1` through `7`. |
| `components.controlPlane.toolResultArtifacts.maxBytes`      | `TOOL_RESULT_ARTIFACT_MAX_BYTES`      | `2097152` | Maximum uncompressed complete-result artifact size, up to 2 MiB.                |

The Workflow options API returns each effective value as a singleton policy list. Legacy mutation fields remain accepted for compatibility but cannot override deployment configuration.

## Audit logging lifecycle

Workspace audit logging is deployment-wide. There is no workspace-level or user-level override.

| Helm value                   | Rendered environment variable    | Default      | Purpose                                                            |
| ---------------------------- | -------------------------------- | ------------ | ------------------------------------------------------------------ |
| `auditLogging.mode`          | `WORKSPACE_AUDIT_LOGGING_MODE`   | `read_write` | Controls future workspace audit event persistence.                 |
| `auditLogging.retentionDays` | `WORKSPACE_AUDIT_RETENTION_DAYS` | `365`        | Purges persisted workspace audit events older than this many days. |

Supported modes are:

* `read_write`: persist read and write audit events.
* `write_only`: persist only audit events classified as `operation: "write"`.
* `disabled`: persist no future workspace audit events.

Retention always runs for persisted rows, even when logging mode is `disabled`. Audit metadata remains sanitized before persistence, so raw tokens, secrets, message bodies, pod logs, auth headers, and full tool arguments are not stored.

## Additional CA trust

Use an additive CA bundle when server-side components must reach OIDC, provider, webhook, MCP, registry, or other HTTPS endpoints signed by an organization-private CA.

For Kubernetes, configure one namespace-local reference:

```yaml theme={null}
global:
  trust:
    additionalCaBundle:
      configMapKeyRef:
        name: organization-platform-trust
        key: ca-bundle.pem
```

Use `secretKeyRef` instead when required by your distribution policy. A component-specific reference under `components.controlPlane`, `components.executionEngine`, or `components.llmGateway` replaces the global bundle for that component.

For VM Compose, set `ADDITIONAL_CA_BUNDLE_SOURCE_PATH` to the host PEM bundle used by the trust overlay. Target connectors use their own settings described in [Connect a Kubernetes cluster](/use/connect-kubernetes#private-platform-ca) and [Connect a Linux VM](/use/connect-vm#agent-behavior).

Additional CA trust:

* extends normal public roots,
* keeps certificate and hostname verification enabled,
* is separate from internal service mTLS,
* does not enable verified TLS for Postgres or Redis unless their URLs request it,
* does not configure container-runtime or OCI registry trust.

The selected Kubernetes resource must exist in the release namespace. Restart affected workloads after bundle changes and rotate roots with an old/new overlap.

## MCP egress policy

Remote MCP servers can be installed at workspace scope for Agent and Workflow use, or at exact target scope for one Kubernetes cluster or VM. In production, the gateway should require HTTPS and block private, local, and reserved network targets unless you intentionally allow specific hosts.

Use allow-lists for trusted internal MCP endpoints instead of broad private-network access.

| Kubernetes value                                       | VM Compose variable                 | Purpose                                                                                          |
| ------------------------------------------------------ | ----------------------------------- | ------------------------------------------------------------------------------------------------ |
| `components.llmGateway.mcpEgress.allowedHosts`         | `MCP_EGRESS_ALLOWED_HOSTS`          | Comma-separated allow-list for remote MCP hosts.                                                 |
| `components.llmGateway.mcpEgress.allowPrivateNetworks` | `MCP_EGRESS_ALLOW_PRIVATE_NETWORKS` | Allows private address ranges when deliberately enabled.                                         |
| `components.llmGateway.mcpEgress.allowLocalAddresses`  | `MCP_EGRESS_ALLOW_LOCAL_ADDRESSES`  | Allows loopback or local targets for reviewed non-production setups.                             |
| `networkPolicies.vault.to`                             | N/A                                 | Egress destinations for private Vault backends.                                                  |
| `networkPolicies.extraEgress.llmGateway`               | N/A                                 | Additional gateway egress for private MCP targets, webhook targets, or approved provider routes. |

Remote MCP server `publicHeaders` are for non-secret metadata only. Credentials belong in secret-backed auth fields, and platform scope headers are reserved.

MCP registry policy uses `components.llmGateway.catalog`. The Official MCP Registry is disabled by default and must be enabled explicitly. Configure internal registries through `bootstrapSources`, use `secretKeyRef` for registry credentials, and keep bootstrap routing set to `direct`. MCP installations select workspace-managed or individual credential ownership and require no deployment-level callback configuration. See [MCP registries](/use/mcp-registries) for complete examples and lifecycle behavior.

## Webhooks

Webhook signing secrets are generated per subscription and returned only once at creation time. The control plane stores encrypted webhook secrets and signs deliveries with HMAC-SHA256.

Webhook events and delivery jobs are durable in control-plane Postgres. Every
control-plane replica can claim work with expiring database leases; Redis is not
required for webhook delivery. Delivery remains at least once, so consumers
must handle duplicates and verify signatures before processing payloads.

| Kubernetes value                                                       | Local or VM Compose variable              | Default | Purpose                                                                                            |
| ---------------------------------------------------------------------- | ----------------------------------------- | ------- | -------------------------------------------------------------------------------------------------- |
| `components.controlPlane.webhookDelivery.enabled`                      | `WEBHOOK_WORKER_ENABLED`                  | `true`  | Enables new delivery-job claims. Set to `false` during maintenance without stopping event enqueue. |
| `components.controlPlane.webhookDelivery.batchSize`                    | `WEBHOOK_WORKER_BATCH_SIZE`               | `50`    | Maximum jobs claimed in one worker batch.                                                          |
| `components.controlPlane.webhookDelivery.concurrency`                  | `WEBHOOK_WORKER_CONCURRENCY`              | `20`    | Maximum concurrent deliveries per control-plane worker.                                            |
| `components.controlPlane.webhookDelivery.perOriginConcurrency`         | `WEBHOOK_WORKER_PER_ORIGIN_CONCURRENCY`   | `4`     | Maximum concurrent deliveries to one destination origin.                                           |
| `components.controlPlane.webhookDelivery.maxAttempts`                  | `WEBHOOK_MAX_ATTEMPTS`                    | `10`    | Maximum delivery attempts before a job becomes terminal.                                           |
| `components.controlPlane.webhookDelivery.maxRetryAgeSeconds`           | `WEBHOOK_MAX_RETRY_AGE_SECONDS`           | `86400` | Maximum retry age in seconds.                                                                      |
| `components.controlPlane.webhookDelivery.maxPayloadBytes`              | `WEBHOOK_MAX_PAYLOAD_BYTES`               | `65536` | Maximum serialized webhook payload size.                                                           |
| `components.controlPlane.webhookDelivery.maxSubscriptionsPerWorkspace` | `WEBHOOK_MAX_SUBSCRIPTIONS_PER_WORKSPACE` | `100`   | Maximum webhook subscriptions in one workspace.                                                    |

The effective per-origin concurrency is the lower of the global worker
`concurrency` and `perOriginConcurrency`. Claim leases account for the time a
full same-origin batch may wait behind that effective limit. The control
plane's delivery deadline covers DNS resolution, connection setup, and the
complete response body, so a stalled resolver or streaming peer cannot retain
a delivery past its lease budget.

Webhook delivery URLs must use HTTPS. Public destinations work by default. Private-address delivery requires an explicit hostname allowlist in addition to any packet-level egress rule:

| Kubernetes value                                            | Local or VM Compose variable                | Default             | Purpose                                                                                                                |
| ----------------------------------------------------------- | ------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `components.controlPlane.webhookEgress.allowedPrivateHosts` | `WEBHOOK_EGRESS_ALLOWED_PRIVATE_HOSTS_JSON` | `[]`                | Allows exact DNS hostnames or leading-wildcard hostname patterns to resolve to private addresses for webhook delivery. |
| `networkPolicies.webhooks.to`                               | N/A                                         | Deployment-specific | Allows the corresponding Kubernetes destination selectors or CIDRs at the network layer.                               |

For a private Mattermost bot at `mattermost-bot.internal`, configure the application-level hostname exactly:

```yaml theme={null}
components:
  controlPlane:
    webhookEgress:
      allowedPrivateHosts:
        - mattermost-bot.internal
```

For local or VM Compose, use the equivalent JSON array:

```dotenv theme={null}
WEBHOOK_EGRESS_ALLOWED_PRIVATE_HOSTS_JSON='["mattermost-bot.internal"]'
```

An exact entry matches only that hostname. A leading `*.` entry matches subdomains, including deeper descendants, but not the bare suffix. The allowlist is additive: public destinations remain available. AcornOps still rejects HTTP, embedded credentials, IP-literal URLs, localhost, metadata services, hard-blocked reserved destinations, mixed allowed/disallowed DNS answers, and redirects. It validates DNS and pins an allowed address before connecting. There is no broad insecure-development webhook switch.
