Agent provisioning flow
Overview
Console saves Slack/model Secrets and submits inline Configuration, credential references and retained Preset bindings. OCC queues setup without placeholders. The worker creates Configuration/Agent, grants exact credential access and admits the first deployment. Its handle follows activation or failure; later deployments use separate handles. See worker reconciliation and Harness topology.
Entry Points
- Console:
apps/controller/src/console/agents/create.mts, with the shared Slack Secret select/create modal inapps/controller/src/console/channels/slack.mjs. - API:
packages/contracts/src/api/routes.ts:provisionAgent,apps/controller/src/http/agents.ts:createAgentHandlers, andpackages/occ/src/index.ts:OpenClawController.provisionAgent. - Preconditions: a ready Namespace, Dedicated runtime and selected Drivers, PostgreSQL-backed work storage, required Agent/Configuration/deploy permissions, exact Secret access and existing transactional IAM authority. No provisioning-input keyring is required.
Flow
graph TD
Console["<b>Console</b><br/>Save entered Secrets"] --> Secrets["<b>Existing Secrets API</b><br/>Return ordinary references"]
Secrets --> API["<b>Provision API</b><br/>Inline config and references"]
API --> Queue["<b>Existing work queue</b><br/>Return job handle"]
Queue --> Claim["<b>Worker</b><br/>Claim and authorize"]
Claim --> Config["<b>Configuration Driver</b><br/>Write or recover Configuration"]
Config -->|Repository selections still valid| Agent["<b>Create Agent</b><br/>Grant exact credential access"]
Config -->|Repository selections refused| RepoRefused["<b>Refuse provisioning</b><br/>No new Agent or revision"]
Agent --> Transport["<b>Compute Driver</b><br/>Trusted-proxy credentials"]
Transport --> Deploy["<b>Ordinary deploy</b><br/>Record first version"]
Deploy --> Observe["<b>Same provisioning handle</b><br/>Observe saved version"]
Observe -->|Pending; version readable| UI["<b>Agent deployment view</b><br/>Open the deploying version"]
Observe -->|Pending; version refused or unavailable| Observe
Observe -->|Success seen in Create| Settings["<b>Agent configuration view</b><br/>Open the activated version"]
Observe -->|Superseded| Superseded["<b>Create stays open</b><br/>Newer deployment took over"]
Observe -->|Malformed present ID| Unknown["<b>Outcome unknown</b><br/>Keep job; offer Retry"]
Observe -->|Confirmed failure| Terminal["<b>Creation failed</b><br/>Keep Agent and version"]
Claim -->|Failure| Failed["<b>Retain outputs</b><br/>Retry known failures"]
Failed -->|Uncertain write| Recovery["<b>Recovery required</b><br/>Do not repeat blindly"]
classDef state fill:#EDF2F7,stroke:#879AB0,color:#25364A,stroke-width:1px
classDef operation fill:#EBF3F0,stroke:#7F9D93,color:#2B4038,stroke-width:1px
classDef condition fill:#F7F1E5,stroke:#B3A078,color:#514532,stroke-width:1px
class Console,Secrets,API,Queue state
class Claim,Config,Agent,Transport,Deploy,Observe,UI,Settings operation
class Failed,Recovery,Terminal,Superseded,RepoRefused,Unknown conditionExecution Trace
1. Console saves credentials and freezes a provisioning request
apps/controller/src/console/agents/create.mts:renderCreateAgent
Console keeps the configured Harness separate from execution mode: dedicated
OpenClaw uses the same provisioning, retry, and deployment navigation as
dedicated Codex. Preset restoration and Credentials read native model runtime
policy through apps/controller/src/console/agents/harness-auth.mjs:configuredHarnessId.
The Slack modal immediately saves new tokens through POST /namespaces/:namespaceId/secrets, before an Agent exists, then clears entered values. Apply stages references and environment bindings; Cancel discards selections but retains created Secrets. Model discovery uses an API key or service account token without saving it. Create Agent runs its preflight, then saves it as a Namespace Secret, clears the input, and reuses the reference on retries. Bound Presets retain their credential/provider. A lost Secret-save response requires recovery, not automatic repetition.
A selected Credential Gateway's catalog directs Console to register the model Secret before source-backed provisioning. Console recovery reuses confirmed sources and safely retries uncertain registration. Installation discovery preserves catalog metadata.
Create Agent sends a stable request ID, inline Configuration, Secret and model-auth references, Agent options, and repository bindings with an explicit access profile. The worker grants Secret access, including Slack. After uncertain admission, Retry sends the same ID and inputs without saving Secrets again.
For a draft, Console creates the Configuration and Agent, then grants access to selected Slack Secrets. Retry credential access keeps that Agent.
2. OCC admits one job
packages/occ/src/index.ts:OpenClawController.provisionAgent
apps/controller/src/http/agents.ts:createAgentHandlers receives schema-validated inputs after shared admission. It supplies the Namespace from the route and creates the audit event inside the controller transaction.
For Harness authentication from a credential source, OCC reads the catalog entry from the recorded Gateway. Compute validates that same entry and its login mode for this admission. A later provisioning request reads the catalog again.
OCC first authorizes Agent create, Configuration create, and Installation administer for the Namespace. Status and retry use the same grants; absence gives 403 regardless of the body. It validates Configuration, references, workspace inputs, execution mode, and current authority, then refuses a taken Agent name with 409. Reserved Secret destinations, cross-Namespace references, and missing or runtime Harness authentication return 400 INVALID_REQUEST naming the rule; an absent Secret remains 404. Before Compute planning, Secret bindings get Agent save's Harness authentication check and named 400.
A selected Credential Gateway requires a supported, ready model Credential Source the caller can operate, and OCC preserves its canonical binding. Unsupported authentication methods fail before a job or external resource is created.
After an Installation selects another Secret Driver, request, status and retry answer 503 for a Secret owned by the previous Driver, after authorization and lookup. The message asks the caller to save replacement Secrets and submit a new request. The worker fails that attempt with PROVISIONING_REJECTED; an unavailable Secret Driver still retries.
OCC applies Configuration rules before Channel credential validation. Its Secret callbacks recheck provisioning grants before storage and after I/O, then confirm current same-Namespace backend and selected-Driver ownership before provider use. Slack accepts whole ${NAME} environment references and checks token roles and bot authentication. This does not pin Secret versions or revalidate queued work. OCC validates Namespace repository selections before queueing. Undeliverable bindings, including Sandbox repositories, retain Agent create's 409 on admission and retry (Compute admission). Initial workspace files require the selected Sandbox's validateWorkspaceSetup; Compute delegates validation or refuses unsupported placement before staging. Deployment checks the Harness topology.
OCC stores the accepted request and deduplication fingerprint in agent_provisioning_work, then queues controller_work with work_kind = 'provisioning'. Configuration and Agent creation happen later. The same actor, Namespace and request ID return the existing work; changed input conflicts.
The 202 response contains data.provisioning, with the work ID and status URL. Public progress exposes result IDs and safe errors without input values or backend credentials.
3. The worker creates resources and credentials
packages/occ/src/index.ts:OpenClawController.processAgentProvisioning
Under its queue claim, the worker verifies ownership, Namespace readiness and authority before effects and result commits. Retries reuse completed outputs. It creates the Configuration from the accepted inline values and bindings, then creates a stopped Agent, records the auth, provider, plugin, repository and workspace selections, and grants its service principal the exact Secret permissions.
The bundled worker uses the Installation's selected repository policy for saved selections and first revision admission. A programmatic controller without this binding uses its registered repository Driver. After fencing the claim and checking that the Namespace is ready, the worker checks the saved selections before creating the Agent. The Configuration Driver may already have written the Configuration. If the check refuses, the worker creates no new Agent or revision and follows the existing Configuration recovery rules.
Source-backed provisioning records the canonical model source and grants only source operate.
Admission, replay, retry and execution recheck caller authority, source readiness and Gateway
ownership. Queued or running work prevents source deletion.
Compute prepares runtime credentials without loopback HTTP. Kubernetes
apps/controller/src/drivers/compute/kubernetes/index.ts:provisionAgentRuntimeCredentials confirms complete owned Secrets after
create-only provisioning. Failure retains them and blocks configured success or
first revision admission.
kubernetesGatewayConfigurationDocument defaults to machine-only trusted-proxy
administration and preserves human identities/scopes. Provisioning and
deploy admission reject an invalid allowUsers or malformed deviceAutoApprove (Configuration
save also refuses both), unknown or padded operator scopes, blank or padded identity keys,
grants for identities outside allowUsers, malformed requiredHeaders, and human
admission with admin device scopes, including whitespace; human allowlists render
x-openclaw-scopes as required. With private status, validateConfiguration
also refuses a Codex Configuration without gateway.auth.password, which the
workspace node check signs in with. These refusals precede workload replacement.
Human ingress and its network policy remain operator-managed; no OCE human
account is required. See Gateway authentication.
Provisioning supplies no Gateway token or trust override; losing its claim cancels
Kubernetes Configuration requests.
4. Deployment becomes the lifecycle owner
packages/occ/src/index.ts:observeProvisioningDeployment
At handoff, provisioning saves the first Agent revision ID and stays running.
Provisioning waits without using attempts. On success, PostgreSQL
marks provisioning succeeded.
If the claim has an audit attempt ID, PostgreSQL saves openclaw.controller_work.completed
in the same transaction.
The job reports the wait, success, or a safe deployment error. During rollout,
Stop gives REVISION_STOPPED. Stop or Delete before handoff gives
PROVISIONING_CANCELLED with its action. Agent and revision IDs survive restart.
Create Agent (apps/controller/src/console/agents/create.mts:waitForProvisioning) polls the job.
When a pending job names its Agent and first revision, Create gives the deployment read one
second to finish. If it succeeds, Create stops polling and opens that revision's Deployment tab
for progress, Stop, and recovery. A 403 or 404 keeps Create on the job with generic status and
ends deployment checks for this attempt. If the read is unavailable or times out, Create shows
any wait reported by the job and checks the deployment again on the next poll. A 401 sends the
caller to sign in again. If Create sees job success before opening Deployment, it opens Configuration.
The Latest visible deployment summary shows the version's recorded wait, even with Deployment details folded. A temporary read failure labels a retained wait as last recorded. A refused read clears it.
Admission, status, and retry report the stored client requestId.
If absent or different, Create keeps the original request and any confirmed job.
meta.requestId identifies each HTTP request. Leaving stops polling, not provisioning.
If Create sees that a newer deployment took over, it explains what happened without a retry
or a link. The job identifies only the deployment that ended. For a failure, follow the linked
version's recovery guidance. After a Stop, deploy when ready.
Reconciliation owns startup and activation; later versions use Deploy.
Console parses each reply in
apps/controller/src/console/agents/provisioning-job.mts:provisioningJob before
apps/controller/src/console/agents/create.mts:submitProvisioningAttempt selects
recovery. A malformed present agentId or revisionId makes even a terminal
failed reply unknown. The form keeps the accepted job URL and original request,
disables Create, and offers Retry without a recovery link. Retry posts to the same
job's /retry. packages/occ/src/index.ts:retryAgentProvisioning refuses a changed
Agent lifecycle or an existing deployment handoff with 409. After refusal during
unknown-outcome recovery, the form enables a resend of the original request ID.
With valid IDs, a failure whose message is absent or not a nonempty string stays
confirmed. ProvisioningFailure uses “Provisioning did not complete.” while keeping
the failure code and any applicable recovery link. For a confirmed failure with a linked
version, follow its recovery guidance.
Dedicated OpenClaw admission requires native worker support and a Sandbox Driver
with all required containment facets. The pinned runtime declares native support;
custom images still require an operator declaration in
Installation startup configuration.
packages/occ/src/index.ts:requireDedicatedNativeSupport enforces both requirements.
Admission does not prove workload delivery. Sandbox receives the setup identity and Secret
reference through HarnessWorkloadRequirements.workspaceSetup; OpenShell runs
workspace initialization
through ExecSandbox before Harness startup.
Stock OpenShell rejects Secret-backed environment projection; the
OpenShell flow
covers remaining limits and verification-only support.
5. Failures preserve useful outputs
packages/occ/src/state/postgres-state.ts:provisioning
Safe failures retry with a fresh claim/authorization, reusing completed resources. Unresolved writes retain target/ownership evidence; expiry or not-found alone does not permit redispatch. Exception: Configuration creates absent after 90 seconds are resent. Only pre-record Configuration refusals roll back, deleting the written Configuration or marking it for later cleanup.
Provisioning blocks conflicting edits and manual deployment. Stale workers
cannot hand off. Stop or Delete saves openclaw.agents.provision.cancelled
with cancellation if request audit is enabled or the claim has an audit
attempt ID. The event saves workId and available request and attempt IDs.
Agent deletion defers runtime credential removal for one lease without spending
attempts. It saves the receipt with deletion. Deploy, Agent update, and
runtime-credential requests return 409 until lease expiry, then settle
the effect. Namespace deletion waits for queued or running work and effects
without receipts. Namespace Secrets and completed Configurations remain.
Requests have no delete API; Agent deletion removes its requests. Agent-less requests
remain; for migration 0049 blockers, see legacy bindings.
Debugging and Verification
- Poll
data.provisioning.urlorGET /namespaces/:namespaceId/agents/provision/:workId; failed work reports a safe error. Status checks stored ownership and provisioning grants, while retry also revalidates the accepted plan. See admission for refusal rules. Explicit pre-handoff retry uses the same URL plus/retryand an empty body; only the initiating actor can read or retry work. Match request IDs inagent_provisioning.compute_refusedandworker.completed. - Status does not repeat repository admission, plugin policy, runtime-image support, Compute plan validation or Harness authentication checks. For source-backed Harness authentication, it omits the Gateway catalog lookup but still requires a ready source, its original Gateway owner, a Sandbox, current operator grants and the Agent grant when the Agent exists. If the Agent already exists, retry rechecks its repository selections before the lifecycle checks and can refuse a readable record. Missing accepted Secrets, Credential Sources or ServiceAccounts give
409; changed selected Driver IDs or Sandbox containment can give503. Caller-owned Configuration refusals name the setting without its value; other Compute refusals use fixed public text. A worker refusal fails that attempt withPROVISIONING_REJECTEDafter inspecting earlier unsettled writes; an unobservable write still retries. Kubernetes provisioning requires explicitdedicated, because omission defaults toembedded. An accepted account without an access token requires the ChatGPT Backend. - Inspect
worker.completed,worker.errorand theagent_provisioningwork metric. PostgreSQL job state lives inocc.controller_workandocc.agent_provisioning_work. - Use
tests/integration/postgres-agent-provisioning.test.tsfor persisted admission, deduplication, safe retry, retained outputs and authorization behavior. - Use Console browser coverage for channel Secret creation before provisioning, reference reuse after failure and job-to-deployment navigation. The disposable Kubernetes fixture proves actual Driver handoff, not native enrollment, model execution or Slack replies.
