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
- Trigger:
POST /namespaces/:namespaceId/service-accounts, thenPOST /namespaces/:namespaceId/service-accounts/:serviceAccountId/credentials, Agent association, and deployment. - Sources:
apps/controller/src/server.mjs:start,apps/controller/src/drivers/service-account/chatgpt.ts:ChatGPTServiceAccountDriver,packages/occ/src/index.ts:OpenClawController. - Requires PostgreSQL, a ready Namespace, exact OCC permissions, the selected ChatGPT Backend and member ServiceAccount Driver, an API-only credential for the configured ChatGPT workspace, and a dedicated Codex runtime for managed access-token deployment.
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"]
endExecution 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:
codex -c cli_auth_credentials_store=file \ -c forced_chatgpt_workspace_id="<workspace-id>" login --with-access-tokenIt clears the token environment and starts its authenticated app server. Refresh, rotation, and automated reconciliation remain deferred.
Debugging and Verification
- Run
node --test tests/integration/service-account-driver-real.test.mjswithOCC_TEST_CHATGPT_SERVICE_ACCOUNT_REAL=1, a protected admin-key file,OCC_TEST_CHATGPT_WORKSPACE_ID, disposable Kubernetes/PostgreSQL, and real digest-pinned OpenClaw/Codex images; do not useOPENAI_API_KEY. - Verify the provider account, private credential ID, exact-account Secret, revision-scoped Codex-only projection, and genuine model response. For ownership, compensation, and ambiguous commits, see the service-account guide and security model.
