Configuration Driver and Agent Revision Flow
Overview
Authenticated API requests create, read, replace, or delete exact Namespace-owned
native OpenClaw configuration documents through the selected bundled or installed
Configuration Driver. The bundled Kubernetes implementation persists each
document in a tenant-owned ConfigMap.
Each Configuration requires immutable kind: "agent" and a server-managed
generation; documents preserve unresolved inline SecretRefs without interpreting
them. Agent deployment authorizes the separate Secret bindings, applies any
selected Sandbox Driver transformation, and saves the validated values in the
Agent revision. Configuration identity, kind, generation, and Secret
references are recorded separately. This flow stops when OCC persists that
version and hands off workload reconciliation; the selected Compute Driver
then owns the Agent gateway, while worker execution and secret resolution remain
outside this flow's scope.
Entry Points
- Startup: controller server and
controller worker read the absolute
OCC_CONFIG_PATH; production requires the file, while development may use its explicit development defaults. - Source:
apps/controller/src/composition/installation-config.ts:loadInstallationConfigurationandpackages/occ/src/index.ts:OpenClawController.createConfiguration. - Requests: Configuration routes
accept
POST /namespaces/:namespaceId/configurationsandGET,PATCH, orDELETEof/namespaces/:namespaceId/configurations/:configurationId. - First deployment:
apps/controller/src/http/agents.ts:createAgentHandlersdispatchesdeployAgentFirstandrecoverAgentFirstDeploymentto OCC. Both require exact Agentdeployand reviewed Configurationread; fresh admission also runs ordinary deployment checks. - Assumptions: one persisted Installation, a selected IAM Driver, exact Namespace placement, an authorized principal, available PostgreSQL state, and the permissions required by the selected Configuration Driver. The bundled Kubernetes Driver requires namespaced ConfigMap CRUD permission.
Flow
graph TD
A["API or worker startup"] -->|reads absolute path| B["Trusted Installation YAML"]
B --> C{"Closed Driver schemas valid?"}
C -->|no| X["Reject startup"]
C -->|yes| D["Construct selected Compute and Configuration Drivers"]
D --> E["Resolve singleton Installation and construct IAM"]
E --> G["Accept exact Namespace Configuration request"]
G --> H{"IAM authorizes exact resource?"}
H -->|no| Y["Reject without cross-Namespace access"]
H -->|yes| I["Lock PostgreSQL ownership metadata"]
I --> J["Validate agent kind and generation; invoke the selected Configuration Driver"]
J --> K{"Agent deployment references Configuration?"}
K -->|no| L["Return authorized Configuration response"]
K -->|yes| M["Authorize and lock exact Configuration"]
M --> P["Authorize Secret bindings and verify backend references"]
P --> Q["Apply optional Sandbox transform; validate configuration and Harness"]
Q --> N["Freeze admitted values, Configuration metadata, and Secret references"]
N --> O["Hand off saved version to reconciliation"]Execution Trace
1. Construct the selected Installation Configuration Driver
apps/controller/src/composition/installation-config.ts:loadInstallationConfiguration
The API and worker load trusted Installation selections and validate the chosen Configuration Driver's schema and implementation-owned rules before constructing its bundled or installed implementation. OCC receives that exact capability and identity; invalid settings or a mismatched factory reject startup. Installation settings never come from a Configuration Driver. The Driver package loading flow owns package identity, validation, trust boundaries, and IAM construction.
2. Resolve singleton state and select Drivers
apps/controller/src/composition/production.ts:composeProduction
Production composition loads the sole persisted Installation from PostgreSQL platform state. Both processes use the same startup document and Installation identity. The Driver loading flow traces process-local construction and the selected Secret, Sandbox, and ServiceAccount branches. Existing AgentRevisions retain their selected Compute identity and immutable admitted configuration; subsequent Configuration edits apply only to later deployments.
Provisioning's KubernetesConfigurationDriver.createExact and inspectExact
use the same verified CP namespace as ordinary Configuration CRUD. Recovery checks
the exact identity and document there; an adopted data-plane namespace does not
change canonical Configuration ownership. When a create's outcome is unknown and
inspectExact still finds nothing 90 seconds after the provisioning effect began
(database clock), the worker fences again and sends the same create under the
same effect; earlier attempts wait without spending a retry, and a Namespace
deletion keeps waiting meanwhile. The Driver's write deadline is 10 seconds and
the API server's default request timeout 60 seconds, so a late original then
fails AlreadyExists. The window covers the first send only: later attempts
resend at once. A failed resend settles the effect only if inspectExact finds
the exact Configuration.
3. Authorize the exact Namespace Configuration operation
packages/occ/src/index.ts:OpenClawController.createConfiguration
Configuration route contracts
define requests the controller request handler
dispatches to createConfiguration, getConfiguration, updateConfiguration,
or deleteConfiguration in OCC. OCC resolves
the server-owned Namespace and checks the selected IAM Driver before accessing
tenant data. Create checks Configuration permission at Namespace scope. Read,
update, and delete target the exact Configuration in that Namespace.
POST and PATCH accept up to 6 × (1 MiB + 32 KiB) for the Kubernetes Driver's
1 MiB of values and the envelope in six-byte JSON escapes. onRequest runs
authorizeConfigurationWrite before reading the body. Without the grant, callers get 403
before body errors. OCC rechecks in the transaction.
For every JSON route, createFastifyApp registers
apps/controller/src/http/json-depth.ts:tooDeepJsonPath to scan raw text once.
A value more than 24 levels below the root gets 400 INVALID_REQUEST with
TOO_DEEP before parsing or validation. Otherwise Fastify parses the JSON.
The text validator visits one entry at a time and checks each key before its
value. It stops at the first NUL (INVALID_FORMAT) or unpaired UTF-16 surrogate
(INVALID_VALUE) and returns 400 INVALID_REQUEST with a capped JSON Pointer.
To identify caller-supplied IDs for a denial, the controller checks at most 4,096
path, query, and body entries, including reference-list items.
Creation requires kind: "agent" and a native JSON object. Missing or unknown
kinds and malformed requests fail with 400. Updates require the complete replacement
values document and accept optional secretBindings and expectedGeneration, refusing
a stale one under the row lock with an /expectedGeneration 409 detail.
On Kubernetes Compute, requireChannelSecretBindings rejects enabled channels'
unbound credential references and Slack token SecretRefs unreadable by the Slack
Channel Driver. kind, generation, and ownership remain server-owned.
Preset admission defers renderer-located whole-string
Gateway-password variables; Configuration writes
and deployment require concrete references. Malformed, generation-mismatched,
or ownership-invalid ConfigMaps fail with 503.
Authorization denial fails with 403, a missing exact resource with 404, an
Agent dependency conflict with 409, and unavailable IAM or storage with
503. OCC checks operate on each bound Secret, including bindings retained when
PATCH omits secretBindings. An empty object ({}) clears the bindings. A binding
with a reserved or invalid destination, or a Secret reference to another Namespace, fails
with 400 INVALID_REQUEST and a message naming the rule. A reserved destination
is also named, with a /secretBindings/<key> detail; a malformed one is not
echoed. A Secret the Namespace does not hold stays 404. The
Configuration reference owns
the binding contract, and the Secret flow
traces storage and delivery. Secret Broker substitution remains unimplemented.
4–5. Persist Configuration and freeze its version
apps/controller/src/drivers/configuration/filesystem/index.ts:FilesystemConfigurationDriver.write
Default Compose development uses the filesystem Driver. Each write exclusively
creates a private temporary file under the exact Namespace directory. It sets the
open file to 0600 before writing so umask cannot make a successful replacement
unreadable. It then writes the approved document, closes the file, and atomically
renames it to the Configuration path. A finally block removes only that write's temporary file, including after
partial writes such as ENOSPC or a failed rename. The prior destination stays
intact until rename succeeds, and other writers' temporary files remain untouched. Storage
and cleanup failures propagate to the caller; this does not add automatic retries
or alter OCC's metadata transaction and compensation boundary.
Configuration persistence and version snapshots traces metadata locking, Driver effects, Agent reference resolution, and snapshot validation after request authorization.
Ordinary deployment retains validated Configuration and Driver identities. Admission rejects changed Agent bindings, Configuration content or generation, or selected Drivers before recording revisions and work. Provider calls remain outside the transaction.
Debugging and Verification
tests/integration/occ-api.test.mjs listens on loopback and uses Slack validation
with a synthetic sender. It covers one revision/work/success audit,
receipt recovery without extra calls, ordinary and disabled-Slack deployment,
Secret authority, Configuration/Driver drift, and late dependency failures.
tests/integration/postgres-platform-state.test.ts separately proves persisted
recovery through a reopened adapter, reconcile counts, and one successful deploy
audit. Its sender is synthetic.
Run focused Configuration conformance and integration checks:
node --test tests/conformance/configuration-occ.test.ts tests/conformance/kubernetes-configuration.test.ts tests/conformance/filesystem-configuration.test.tsnode --test tests/integration/configuration-controller.test.mjs tests/integration/postgres-platform-state.test.tsnode --test tests/integration/postgres-platform-state-kubernetes.test.tsPostgreSQL integration requires configured database URLs; skipped cases do not
prove live persistence. Live ConfigMap and least-privilege RBAC proof requires
a disposable Kubernetes cluster and tenant credentials; client fixtures do not
replace that evidence. Startup emits structured
startup-error or worker.startup-error events for invalid YAML and
unavailable dependencies. Verify missing or unknown kinds are rejected,
creation starts at generation 1, updates advance it once without permitting a
kind change, requests preserve the exact native document and inline references,
and ConfigMaps store exactly one openclaw.json entry. Verify denials against
exact Namespace and Configuration identity. Prove snapshot safety by changing
a nested Configuration value after deployment and confirming the prior
Agent revision's native document, Configuration identity, kind, and generation
remain unchanged while a later deployment observes the new generation. Verify
same-Namespace Agents receive separate gateways only when an eligible
development worker prepares their versions. Kubernetes conformance verifies
immutable Agent-owned configuration projection without a live cluster;
dedicated-cluster coverage of the changed projection remains unverified when
its optional integration is skipped.
Related docs
- Installation Driver package loading flow
- Configuration Driver guide
- Controller and startup configuration
- Controller lifecycle
- Agent lifecycle
- Kubernetes Compute Driver
- Configuration Driver implementation specification
- OpenClaw-native Configuration specification
- Configuration kind and Agent-owned gateway specification
