Credential sources
A Secret holds a value, such as a GitHub App private key. A credential source
records its Driver, configuration and Secret references in the Namespace. A
github-app-metadata source names the key and repository for the read of
acme/widgets.
The Agent draft binds the source by ID. On deploy, OpenClaw Control Plane (OCC) saves the binding in an Agent revision. How the sender gets a credential depends on the source.
| Delivery mode | Who holds or reads the credential |
|---|---|
| Gateway copy | The selected OpenShell Credential Gateway stores a copy of a static Secret. The workload receives a placeholder. Its supervisor injects the real value on eligible traffic. Editing the Secret alone leaves the copy unchanged. |
| Gateway-managed OAuth | The Credential Refresh Driver holds issuer material and manages access tokens for the Gateway. The workload keeps its placeholder across token renewal. |
| Protected live Secret | After OCC authorizes an individual request, the trusted resolver reads the current Secret version. The authenticated sender receives the credential header for that request. An existing Gateway source requires explicit adoption. |
| Worker-prepared token | For github-app-metadata, the Token Service renews installation tokens in the background. An authorized Credential Proxy request can also start or join issuance for its exact registered grant. |
| Credential-free metadata | The source admits a specific repository identity read. Its resolver reads no Secret and its sender adds no credential header. Request and response controls still apply. |
Gateway delivery supports openai API keys for dedicated Codex and native
OpenClaw models, codex-pat for dedicated Codex and bearer-token for a
single API endpoint. Selecting a Credential Refresh Driver also enables experimental
codex-oauth device login, oauth2-client-credentials and oauth2-refresh-token.
Device starts commit audit with source and session writes; see the
device authorization flow.
OpenShell is a development integration. See the OpenShell qualification requirements, Credential Proxy overview and threat model.
Register a source
- Wait for the Namespace to become
ready. - For a Secret-backed source, create a Secret and keep its
ref. - Send
POST /namespaces/:namespaceId/credential-sources. The caller needscredential_source:createon the Namespace andsecret:operateon each referenced Secret. A protected metadata source requiresagent:operateon the active, dedicated Agent named byconfig.agent_idin that Namespace.
Replace the sample IDs and Secret reference. Execution authority admits every process in that execution.
{ "name": "widgets-metadata", "type": "github-app-metadata", "config": { "app_id": "1001", "installation_id": "2002", "repository_id": "12345", "repository": "acme/widgets", "agent_id": "agt_123e4567-e89b-42d3-a456-426614174000", "authority": "execution" }, "secrets": { "private_key": { "kind": "secret", "namespaceId": "ns_123e4567-e89b-42d3-a456-426614174000", "id": "sec_123e4567-e89b-42d3-a456-426614174000" } }}The 201 response includes the cs_ source ID, binding ref and status. Bind the Agent to the source ID. The source retains the sec_ reference.
nameis unique within the Namespace.- Select an offered
type. With a Proxy selected, fresh Gateway registration requires a static Harness model type and the bundled Proxy handling only Git. Refresh, device authorization and ChatGPT OAuth are ineligible. Other registrations require protected admission. Explicit live adoption preserves ownership. configcontains nonsecret catalog fields. OpenShell'sopenaitype acceptsauthorized_base_urlas immutable destination authorization. Omission authorizes onlyhttps://api.openai.com/v1. Custom authorization must match the revision provider at deployment and delivery.base_urlcannot select a route. Replace old sources before reuse.secretsmaps catalog fields to same-Namespace Secret references. OCC rejects cross-Namespace references with400and missing Secrets with404.
OCC rejects unknown or missing required fields and invalid configuration
with 400 INVALID_REQUEST, which names the field and points at /config/<field> or
/secrets/<field>, before reading Secrets through
the Secret Driver. OCC records registering before the external operation.
For Gateway copies, definitive rejection triggers cleanup. An uncertain write,
such as a timeout, leaves the source deleting because a copy may still
appear. Use DELETE to finish cleanup.
Secret-backed refresh sources complete registration only after the gateway
mints the first token. If the issuer refuses the material or cannot be reached,
registration fails with 503 naming the Driver's failure code, and OCC removes
the source. Device-authorized sources register before login; their live status
stays pending until login and managed-token setup complete.
Read and list sources
GET /namespaces/:namespaceId/credential-sources/:credentialSourceId requires
exact read. A Gateway copy reports the Gateway's live status as ready,
pending, failed or absent, with an optional reason. A Gateway outage
produces failed status on a successful read. For a refresh source,
status.refresh reports state, expiry and refresh times, and any failure code
and recovery action, never a token. A prepared source can have record state ready and live
status pending while its worker prepares the token.
GET /namespaces/:namespaceId/credential-sources requires Namespace read
and returns only sources on which the caller has exact read. Lists
do not call the gateway and omit status.
The record's state is registering, ready, or deleting. Bind or deploy only ready sources. Delete interrupted registrations to remove any Gateway copy.
Bind a source to an Agent
Set credentialSources to a list of up to 64 distinct
{ "sourceId": "cs_…" } entries. On update, the list replaces the draft's
bindings. Omit it to keep them or send [] to remove them all.
| When | Required source permissions |
|---|---|
| Create or update the Agent | The caller needs the corresponding agent:create or agent:update and credential_source:operate on each current and requested source, including removals. |
| Deploy | The caller needs agent:deploy. Both the caller and the Agent service principal need credential_source:operate on each listed source. The worker checks both again before provisioning. |
| Use a protected source | OCC checks the Agent service principal's current credential_source:operate before it releases material and again before it authorizes the send. |
Grant the Agent service principal access to the source with a Namespace IAM Role and AccessBinding. The Agent needs no permission on the underlying Secret. Each requested source must be ready and its recorded owner selected. Newly registered metadata sources belong to the Credential Proxy. A Gateway source retains its Gateway owner even when the Proxy enforces its plan.
Deployment with credential sources requires a selected Sandbox Driver. The revision records each source's
ID, type and owning Driver ID. For the widgets-metadata example, the protected
plan allows GET /repos/acme/widgets for that execution and source generation. Follow the
request to GitHub and its reply.
Cloning, pushing or creating a pull request requires a separately admitted
repository binding.
For source-backed Harness authentication, set harnessAuth to
{ "method": "credential_source", "sourceId": "cs_…" }. Choose a listed
Gateway source whose catalog type supports Harness authentication. Naming an
unlisted source, or removing it while harnessAuth still names it, fails after the grant checks with 400: "The Harness credential source must be listed in the Agent's credentialSources." When replacing a binding after a Driver change,
update harnessAuth if it names the old source.
Binding and deployment trace
| What the workload may know | What it can do with it |
|---|---|
| Source ID or Secret reference | Identify a record. An ID conveys no permission to use a source or read a value. |
| OpenShell placeholder | Use eligible traffic from the configured Sandbox. The provider requires the real credential. |
| Native repository session bearer | Exercise delegated authority within the live session's scope, subject to the credential service's checks. |
While a Credential Gateway is selected, deployment and guided provisioning reject
api_key and codex_pat bindings (both Secret and ServiceAccount sources) with 409.
Guided provisioning accepts a ready model source in harnessAuth, records it in
the Agent's credentialSources, and grants its service principal exact source
operate access. The caller needs source operate; the Agent receives no
permission on the underlying model Secret. Queued and running provisioning jobs
keep their source referenced before the Agent exists.
The Console reads the Gateway catalog from Installation or Namespace Agent capabilities. Both Dedicated Harnesses share provider and Secret controls. API-key provisioning registers the Secret first. See Console creation.
For an Agent without repositories on the bundled Git-only Proxy, bind the matching ready static Harness model source from the selected Gateway as the Agent’s only credential source. Kubernetes rechecks its lifecycle before release.
Update a source
Device-authorized sources reject PATCH with 409 before reading Secrets or
changing refresh state. Sign in again and replace the Agent binding instead.
For an ordinary static Gateway source, updating the Secret does not change the copy. To push a new
value, send
PATCH /namespaces/:namespaceId/credential-sources/:credentialSourceId. The
caller needs exact credential_source:update and secret:operate on every
Secret the update reads:
- An empty body
{}re-reads the source's current Secrets. Anoauth2-refresh-tokensource refuses it; see below. { "secrets": { "api_key": <SecretReference> } }switches each named field to a replacement same-Namespace Secret. The field set stays the source type's catalog fields, and non-secretconfigcannot change; register a new source instead.
A successful update returns 200 with the source and its live gateway status.
Only a ready source can be updated. A gateway failure returns 503 and leaves
the Secret references unchanged; once the gateway may have changed, the failure
is audited. The gateway is updated before OCC commits, so
if the request fails after that, repeating the same request converges. If the
gateway no longer holds a copy (absent), the update also returns 503;
delete the source and register it again.
Prepared and credential-free sources reject PATCH and rotation with 409. Register a replacement source. For a live static source, changing the referenced Secret value affects subsequent eligible reads. Changing its reference or adopting a legacy source requires an unbound source. See protected static inference.
Migration 0048_administrator_credential_source_grants adds the current
credential_source grants, including update, to an unchanged built-in
Installation administrator Role from an earlier bootstrap. Other Roles keep
their exact grants; grant update through a Namespace Role where needed.
Updated Gateway copies reach new Harness processes only. To rotate a copied key, follow these steps.
- Update the Secret's value (
occ secret update), or create a replacement Secret. - Run
occ credential-source update ID, adding--filewith replacementsecretsif you created a new Secret. - Redeploy each Agent that uses the source.
A Secret-backed refresh source keeps no static value: an update replaces its refresh
material and mints a new token, and OCC commits replacement Secret references
only after that mint succeeds. If the mint fails, the update returns 503, but
the gateway keeps the new material; the source stays ready and its
status.refresh reports the failure. OCC does not restore the previous
material; update again with corrected Secrets. Running Agents lose the source's
token within about 10 seconds of an update, even a failed one, and receive none
until you redeploy them.
An issuer may rotate a refresh token on use. Reusing the recorded Secret can
fail or revoke the sign-in. An oauth2-refresh-token update that retains the
refresh_token Secret, including {}, returns 409. Sign in again, save the
new token in a new Secret, and reference it.
Rotate a refresh source
For sources with rotation: refresh, the Gateway renews the access token before
it expires. Running Agents keep the same placeholder. To rotate immediately, call
POST /namespaces/:namespaceId/credential-sources/:credentialSourceId/rotate
or run occ credential-source rotate ID. Rotation requires exact
credential_source:update, reads no Secret, and needs no redeploy. It does
not revoke the previous token at the issuer.
Success returns 200 with the source and status.refresh. Static sources
return 409; an unavailable gateway or failed mint returns 503. Failed
mints are audited.
status.refresh.recoveryAction: reauthorize means the issuer revoked the refresh
token. Update a Secret-backed source with a new authorization Secret. For device
authorization, sign in again and replace the Agent binding.
Prepared issuers cannot rotate on demand. If their provider advertises
rotation: refresh, enrollment returns 501 before reading Secret material,
configuring the issuer, or calling Credential Refresh.
Withdraw a source from an Agent
Withdrawal revokes a source from an Agent's selected version while the version keeps running, and the source cannot be deleted until a redeploy replaces that version. Credential source withdrawal covers the request, its retries, and maintenance.
Delete a source
DELETE /namespaces/:namespaceId/credential-sources/:credentialSourceId
requires exact delete and returns 204:
- It returns
409 RESOURCE_CONFLICTwhile an Agent draft, selected version, pending deployment, or queued/running provisioning request references the source. Wait for provisioning, remove the binding and redeploy, or delete the Agent. - When only a queued/running withdrawal attempt or retry series blocks it, the
409isCREDENTIAL_WITHDRAWAL_IN_PROGRESS. A withdrawal that never confirms keeps its series queued for up to about an hour, even after redeployment; the request no longer applies once the selected version drops the source. Wait for the series to finish, or delete the Agent: completed deletion drops that work. - With neither a Credential Gateway nor a Credential Proxy selected, deletion returns
409 CREDENTIAL_GATEWAY_NOT_CONFIGURED. Selecting a different Driver for the recorded owner returns503. OCC keeps the source record in both cases. - OCC marks the record
deletingbefore cleanup. For a Gateway copy, OCC asks the Gateway to remove it. A dependency failure returns503; an OpenShell provider ownership mismatch returns404. Both retaindeleting. Resolve ownership drift before retrying; an already-removed copy counts as deleted. - For a prepared GitHub source,
409 CREDENTIAL_TOKEN_UNSETTLEDmeans a token it issued may still be valid: GitHub's answer to an issue or revocation was lost. The record staysdeleting. Retry after the time in the message, at most 72 minutes after the attempt. A refused issue with no token does not block deletion. While the worker is revoking a token, deletion waits up to 25 seconds for GitHub's answer. - Within 70 seconds of registration,
503 DEPENDENCY_UNAVAILABLEkeeps the record against late registration.error.reason: "credential_registration_pending"identifies this refusal;Retry-Aftergives the remaining whole seconds. Wait that long before retrying the same deletion. Other503responses do not authorize an automatic retry.
While a source exists, including one in deleting, its Namespace cannot be
deleted, and its referenced Secrets cannot be deleted.
After a Credential Gateway change
A Gateway-owned source belongs to the Driver that registered it. After the
Installation selects another driver in drivers.credential_gateway, binding,
deploying, updating, or deleting an old source returns
503 DEPENDENCY_UNAVAILABLE with one fixed message: "The selected Credential
Gateway Driver did not register this credential source. …". OCC answers it only
after the caller's grant and the source lookup. GET on such a source reports a
failed status whose reason names the driver change.
- To keep an Agent running, register a replacement source through the selected driver, list it in place of the old one, and deploy again.
- To delete an old source, an administrator reselects its original driver ID,
deletes it, then selects the new driver. This also finishes sources left
deletingby earlier releases, unblocking Namespace and Secret deletion.
Errors
| Status | Meaning |
|---|---|
400 INVALID_REQUEST |
Invalid request, catalog field, configuration or cross-Namespace Secret reference, or an unlisted Harness source. |
403 FORBIDDEN |
A required permission on the source, Secret or Agent is missing. |
404 NOT_FOUND |
The exact Namespace lacks the source, Secret or named Agent, or the selected version has no matching withdrawal. |
409 NAMESPACE_NOT_READY |
Wait for the Namespace to become ready. |
409 AGENT_DELETING |
A protected source names an Agent being deleted. |
409 RESOURCE_CONFLICT |
The type, Agent, Driver selection or requested operation is incompatible, the source is still referenced, or its state changed. The message describes the conflict. |
409 CREDENTIAL_GATEWAY_NOT_CONFIGURED |
The operation requires a Credential Gateway and none is selected. |
503 DEPENDENCY_UNAVAILABLE |
A required Driver or issuer is unavailable, a Gateway operation or mint failed, or the selected Driver differs from the recorded owner. |
