OpenClaw EnterpriseDOCSGitHub

Service Account Driver Credential Delivery Flow

Overview

OCC starts with Installation Backend composition, then creates a Namespace-owned account, separately issues its provider-backed credential, and deploys an associated dedicated Codex Agent. The API owns the Backend client and upstream account calls; worker reconciliation repeats metadata checks before Compute projects the account Secret to Codex. This flow stops after Codex starts with the projected access token and workspace.

Entry Points

Flow

graph TD
  subgraph Composition["Installation composition"]
    A["Validate Backend and selected Driver"] --> B["API builds ChatGPT client"]
    B --> C["Inject Backend into ServiceAccount Driver"]
    A --> D["Worker keeps nonsecret Backend metadata"]
  end
  subgraph API["OCC API"]
    C --> E["Authorize and create OCC account"]
    E --> F["Create upstream account and private binding"]
    F --> G["Authorize separate credential issuance"]
    G --> H["Issue token and store account Secret"]
    H --> I["Persist credential ID and Secret reference"]
    I --> J["Save nullable Agent backendId"]
    J --> K["Validate binding and freeze revision"]
  end
  subgraph Worker["Worker and Compute"]
    D --> L["Reauthorize deployment actor"]
    K --> L
    L --> M["Recheck Backend and binding metadata"]
    M -->|valid dedicated Codex| N["Project account Secret into Codex"]
    M -->|mismatch| O["Fail candidate"]
    N --> P["Codex pins workspace and starts app server"]
  end

Execution Trace

1. Compose the Backend and its ServiceAccount Driver

apps/controller/src/server.mjs:start

loadInstallationConfiguration validates the singular backend array and requires each ChatGPT Backend to declare the selected service_account member Driver. server.mjs:start then reads the mounted apiKeyPath, constructs Backend<ChatGPTClient>, and injects it into the bundled ChatGPTServiceAccountDriver factory. That client and admin key stay on the API side. The worker receives only nonsecret Backend definitions so it can reject stale or mismatched deployment snapshots before Compute effects.

2. Create the account and private Backend binding

packages/occ/src/index.ts:OpenClawController.createServiceAccount

OpenClawController.createServiceAccount authorizes the exact Namespace and allocates its sa_* identity. ChatGPTServiceAccountDriver.create creates the upstream account, registers rollback, and persists its private Backend binding in the same PostgreSQL transaction, including Backend, Driver, Namespace, account, and workspace identity.

Account creation and credential issuance are separate operations. Creating the account does not issue a token, and later issuance or deletion requires that exact binding to match the current configured Backend and member Driver.

Before deletion, OCC locks the Namespace and account, then checks Agent drafts, active revisions, and queued or claimed revision work through serviceAccounts.hasReferences. A conflict returns before any Driver call can revoke the credential or remove its Secret. Namespace locking serializes this check with draft changes and deployment admission; the PostgreSQL reference query observes active pointers and pending work together during worker cutover.

3. Issue the credential and create one account Secret

apps/controller/src/drivers/service-account/chatgpt.ts:ChatGPTServiceAccountDriver.createCredential

OpenClawController.createServiceAccountCredential authorizes account update before ChatGPTServiceAccountDriver.createCredential issues a Codex-scoped token. KubernetesComputeDriver.storeServiceAccountCredential stores it with the workspace ID in one account-owned control-plane Secret. The private credential ID, internal credential reference, and audit changes commit together; confirmed failures compensate created provider and Kubernetes resources.

4. Save Agent Backend intent and admit the revision

packages/occ/src/index.ts:OpenClawController.createAgent, updateAgent, deployAgent

Agent backendId is nullable. Create omission saves null; PATCH omission preserves the current value; explicit null clears it; and a nonnull ID must name a configured Backend. Saving or changing the draft Agent reference makes no upstream call. The Agent selects the issued account through harnessAuth: { method: "chatgpt_service_account", serviceAccountId }.

deployAgent authorizes the Agent, Configuration, and associated account, then validates access_token ownership with validateServiceAccountBackendBinding. Managed access-token deployment requires the exact nonnull Backend, selected member Driver, workspace, account, recorded credential issuance, and dedicated Codex execution. An account without an issued credential cannot deploy. Admission freezes the account identity, exact credential reference, private Backend binding, and Agent backendId in the revision. Supplied API keys use the same harness binding path through an OCC Secret; native account references are not model-auth selectors.

5. Recheck metadata and project the account Secret

apps/controller/src/drivers/compute/kubernetes/index.ts:KubernetesComputeDriver.prepareRevision

ControllerWorker.resolveRevisionBackend runs after IAM reauthorization and before Compute reconciliation. It rejects a missing configured Backend as BACKEND_UNAVAILABLE and a metadata mismatch as SERVICE_ACCOUNT_BACKEND_MISMATCH. Only Backend, Driver, workspace, account, and issuance metadata leave the repository; upstream account IDs, admin keys, and credential values stay private.

KubernetesComputeDriver.prepareRevision uses its internal prepareHarnessAuth rendering step and explicit login mode, then delivers the selected account fields into a revision-owned data-plane Secret for dedicated Codex. Embedded execution is rejected for managed access tokens. The Gateway receives no model credential. The trusted worker reads the CP source and manages the DP projection; workload ServiceAccounts receive no Secret API permission.

6. Authenticate Codex under the exact workspace

apps/controller/src/drivers/compute/kubernetes/runtime-entrypoints.ts:AGENT_RUNTIME_ENTRYPOINT

AGENT_RUNTIME_ENTRYPOINT authenticates with the projected token and workspace:

sh
codex -c cli_auth_credentials_store=file \  -c forced_chatgpt_workspace_id="<workspace-id>" login --with-access-token

It clears the token environment and starts its authenticated app server. Refresh, rotation, and automated reconciliation remain deferred.

Debugging and Verification

Search documentation