The Credential Gateway stores OCC credential sources
as OpenShell providers. The supervisor substitutes credentials outside the Harness.
Select it with the OpenShell SandboxDriver on one openshell
Backend.
OpenShell remains unsupported for production. Workload identity, writable state, authentication, admission, and provider-file proof follow the OpenShell qualification contract.
Protected static inference requires a separate authenticated counterpart.
Select drivers.credential_gateway with the OpenShell Backend and Sandbox in
trusted Installation YAML. The Backend member IDs must match the selected Drivers:
backend:
- id: openshell
type: openshell
configuration:
endpoint: https://openshell-gateway.openshell-system.svc:8080
auth:
mode: mtls
certificatePath: /etc/openclaw/openshell/tls.crt
privateKeyPath: /etc/openclaw/openshell/tls.key
rootCertificatePath: /etc/openclaw/openshell/ca.crt
drivers:
sandbox: openshell-sandbox
credential_gateway: openshell-credentials
credential_refresh: openshell-refresh # enables OAuth2; Codex also needs operatorTls
drivers:
sandbox:
id: openshell-sandbox
configuration:
gateway:
workspaceMode: operator
# See openshell-sandbox.md for the remaining Sandbox settings.
credential_gateway:
id: openshell-credentials
configuration:
binaries:
- /path/to/codex
- /usr/local/bin/node
toolBinaries:
- /usr/bin/curl
credential_refresh:
id: openshell-refresh
configuration: {}binaries requires a nonempty list of absolute executable paths inside the Harness
image. OpenShell releases credentials to those binaries and their child processes.
Use the exact native Codex executable or /usr/local/bin/node for OpenClaw.
The development profile lists Node and both
x64/ARM64 Codex paths independently of the launcher host.
A stale path fails the Harness startup model probe with
RUNTIME_MODEL_PROBE_FAILED, or RUNTIME_AUTHENTICATION_FAILED when the provider
rejects the missing credential, and the current Agent revision keeps serving.
toolBinaries is optional: nonempty absolute paths inside the Sandbox image that
may carry tool sources to
their endpoints, such as /usr/bin/curl. Without it the catalog omits
bearer-token and the OAuth2 types.
drivers.credential_refresh is optional and takes an empty configuration. It
selects the bundled Credential Refresh implementation,
which must be the same Backend's credential_refresh member. With it and
toolBinaries, the catalog adds the OAuth2 types.
Changing binary lists rewrites profiles on the next update, deployment, or
repair; until that succeeds, removed binaries retain access and deployment stays
pending. To cut access immediately, withdraw or delete the source. Mixed-version
controller replicas can overwrite each other's lists. Removing toolBinaries
blocks tool-source registrations, updates, deployments, and repairs with
409 RESOURCE_CONFLICT, because OpenShell treats an empty list as any binary.
Existing sources remain readable and deletable.
Startup rejects the selection when:
- the Installation does not select the bundled Kubernetes Compute Driver;
- no
openshellBackend exists, or itsdrivers.credential_gateway,drivers.sandbox, ordrivers.credential_refreshdiffers from the selected IDs; drivers.credential_refreshis selected withoutdrivers.credential_gateway; or- the configuration has any key other than
binariesandtoolBinaries.
Both API and worker need gateway access with Backend credentials: the API registers/deletes providers; the worker creates Sandboxes, updates profiles and reads attachment status. The selected Refresh Driver owns device exchange and refresh configuration.
| Type | Secret fields | Config fields | Rotation | Harness authentication |
|---|---|---|---|---|
openai |
api_key (required) |
base_url (optional) |
none |
openai / api_key |
codex-pat |
access_token (required) |
None | none |
openai / codex_pat |
codex-oauth (Experimental) |
None; device login | None | refresh |
openai / chatgptAuthTokens |
bearer-token |
token (required) |
host, env_var (required); port, path |
none |
None (tool credential) |
oauth2-client-credentials |
client_secret (required) |
host, env_var, token_url, client_id (required); port, path, scope |
refresh |
None (tool credential) |
oauth2-refresh-token |
refresh_token (required); client_secret |
host, env_var, token_url, client_id (required); port, path, scope |
refresh |
None (tool credential) |
Codex PATs (personal or service-account) use CODEX_ACCESS_TOKEN at these HTTPS routes:
auth.openai.com/api/accounts/v1/user-auth-credential/whoami.chatgpt.com/backend-api/codex/**.chatgpt.com/backend-api/wham/config/bundle.chatgpt.com/backend-api/wham/accounts/check.chatgpt.com/backend-api/wham/settings/user.chatgpt.com/backend-api/wham/usage.
Other routes remain denied.
The Harness stores only an opaque personal_access_token. Readiness requires
native login and model execution
(bootstrap flow).
openai defaults to https://api.openai.com/v1. Custom HTTPS Responses endpoints must end in /v1, optionally after a prefix such as /api. Before Secret reads or persistence, 400 INVALID_REQUEST rejects wildcards, port 0, an @ or credentials, spaces, controls, a missing //, queries, fragments, and a host that is not an exact DNS name, as for bearer-token. OCC stores the normalized, immutable endpoint; register a new source to change it.
Invalid bearer-token and OAuth2 values return field-specific 400 before Secret
reads or persistence:
hostis an exact lowercase DNS name (re-checked at attachment), not an IP address or wildcard.portis 1 to 65535 and defaults to443.pathis an absolute path pattern and defaults to/**.env_varis the upper-case Sandbox variable holding the placeholder, notOPENAI_API_KEY,PATH,HOME,USER,SHELL,LANG, or a name starting withCODEX_,OCE_,OPENCLAW_, orOPENSHELL_.token_urlis anhttpsURL without credentials, query, or fragment. Its host follows thehostrule, has a dot, and is not alocalhostorlocaldomainname. An explicit port must be 1 to 65535.client_idis 1 to 256 visible characters;scopeis space-separated OAuth2 scopes.
The workload sends the placeholder itself:
curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com/v1/items.
The proxy substitutes the token only on requests from toolBinaries to the
source's endpoint. Never put a placeholder, or its openshell:resolve:env: prefix, into the model conversation:
OpenShell refuses a model request whose body carries one, so the Agent's turns
fail until that history is gone.
Registration rejects provider types outside this catalog.
The experimental codex-oauth type uses upstream GetProviderCredentials and
matching supervisor/static launcher binaries. Select paired credential_refresh
and configure operator TLS and issuer trust.
The pinned upstream binaries implement the required contracts.
Ordinary Backend authentication handles other operations. Access-token retrieval
alone uses operatorTls: a trusted gateway-CA certificate with OU=operator
and openshell.gateway.mtls_auth.operator_enabled. It rejects bearer headers and
forwarded identity. Protect its gateway-wide key. OCC checks exact source rights
before retrieval and discovery; Namespace-scoped OpenShell identities remain deferred.
Refresh exchanges the device grant into OpenShell custody; OCC stores an opaque
session. withSourceToken requests five-minute validity; OpenShell may refresh
and persists successors. Tokens enter only the trusted callback, never the Console.
Cancellation cannot undo refresh. Agent revisions reuse metadata and placeholders.
The Sandbox wraps the stable placeholder in the documented JWT alias for Codex's local account metadata. OpenShell replaces the whole alias, enforcing identity, revocation, endpoints and expiry. Selected runtime plugins remain unsupported; directory discovery does not prove execution. See OAuth storage.
- Connection recovery and metadata. Grant configuration and account metadata are separate writes; interrupted handoff may require reconnecting. An initial device-poll timeout closes the session and requires a fresh login. Metadata is a login-time snapshot, so account changes require a new login and Agent revision.
The paired Credential Refresh Driver configures the profile-declared issuer and mints the empty provider's first token. OpenShell holds refresh material and renews before expiry. Its supervisor picks up re-minted tokens on its provider poll (every 10 seconds by default). Reconfiguration starts a new authorization epoch, revoking running Sandboxes' placeholders and requiring redeployment.
The gateway Pod must reach token_url through its NetworkPolicy and trust the
issuer. Preserve public roots when adding a private CA; see
issuer trust setup.
The Refresh Driver owns request IDs and recovery.
Each OCC Namespace maps to an operator-mode Workspace named after its Kubernetes namespace. The Driver manages:
- One provider profile per source. Its ID equals the provider name; a content
digest drives registration/update/deployment reconciliation.
openaibinds bearerauthorizationto its configured host/port/base path plus/**, forbinariesonly.bearer-tokenexposesenv_varat its host/port/path fortoolBinaries.codex-patexposesCODEX_ACCESS_TOKENat the catalog's fixed PAT routes;codex-oauthexposes it for ChatGPT. The Refresh Driver owns issuer exchange and refresh configuration. - One provider per source. Its name is
oce-cs-plus 24 hexadecimal characters of the source ID's SHA-256. Labels identify OCC ownership, source and Namespace;profile_workspacenames its workspace. Registration checks ownership/type before profile mutation and again on creation collision. These calls are not atomic: a provider or orphan profile can change between them. - Workspace. OCC resolves the Namespace's workspace from Compute's runtime placement, so registration uses the same workspace as the paired Sandbox.
sourceStatus reports ready for an owned provider, absent for a missing one,
and failed for a same-name provider the source does not own.
Refresh reports pending during refresh_in_progress/refresh_committing.
A persistent uncertain marker requires inspection; OCC neither replays the
exchange nor reseeds credentials.
updateSource requires the existing provider to be OCC-owned for the exact
source, rewrites the source's profile when the configured binaries changed,
then calls UpdateProvider with the new credential values. UpdateProvider
merges non-empty values, so the driver rejects an empty value rather than
silently keep the old one. OpenShell gives the new value only to processes
started after the update; a running Harness keeps the previous value until it
restarts.
removeSource deletes the owned provider, confirms it gone, then deletes the
source's profile; OpenShell cannot delete a Workspace still holding profiles.
The source ID identifies its profile even with an invalid persisted endpoint. Deletion refuses an unowned provider; unknown source types and gateway errors remain failures.
For an Agent revision, attachForRevision updates each source's profile and returns
its provider name. It fails when two of the Agent revision's sources would use
the same environment variable, and the worker then fails the deployment with
CREDENTIAL_SOURCE_ENVIRONMENT_CONFLICT without retrying. The
OpenShell SandboxDriver appends those names to SandboxSpec.providers.
attachmentStatus calls GetSandboxProviderStatus for each provider and maps
OpenShell readiness states to ready, withheld, revoked, failed, or
pending. withdraw calls DetachSandboxProvider for the Agent revision's Sandbox,
then reads that receipt's status. Only REVOKED reports revoked: the
Sandbox's placeholders then stop resolving, even in running processes. OpenShell
reports REVOKED once the supervisor sees a running process without the
provider. A Sandbox without a running process (provisioning,
crash-looping) reports WaitingForProcess, which stays pending. A recheck
first calls GetSandbox: a Sandbox no longer listing the provider is revoked
without a mutation; a listed one is detached again. A missing Sandbox is
absent only once GetWorkspace answers and a second GetSandbox still misses
it (OpenShell hides unauthorized Sandboxes as missing); a refusal fails with
SANDBOX_GATEWAY_UNAUTHORIZED.
For API-key Codex, Compute selects a named provider with the source endpoint and
HTTPS Responses transport. Native OpenClaw requires the default OpenAI endpoint.
The Harness receives an openshell:resolve:env: placeholder as OPENAI_API_KEY
for API keys or CODEX_ACCESS_TOKEN for Codex PATs. Codex stores it through native
login; OpenClaw reads it through node-local model configuration. The supervisor
substitutes the credential on matching requests.
- Workspace membership. Any OpenShell user in a Workspace can attach any provider in it. Keep OCC's gateway principal as the only member of OCC Workspaces. OpenShell Platform Admins bypass membership in every Workspace, so limit that role as well. OCC does not check either.
- Gateway principal. OCC's gateway principal must be allowed to manage
Workspaces, Sandboxes, and providers. Use
bearerTokenFileand anhttpsendpoint outside disposable development. - Network enforcement. The guarantee depends on OpenShell's NetworkPolicy, which denies workload-initiated connections. The cluster network plugin must enforce NetworkPolicy.
- TLS inspection. The proxy terminates TLS for the selected profile endpoint,
so Codex must trust the Sandbox CA that OpenShell provides through
SSL_CERT_FILE. Do not add an uninspectedtls: skippolicy for the selected model endpoint in the Sandbox'spolicy.networkPolicies; it conflicts with the profile. - Namespaces. Sources never cross OCC Namespaces.
The boundary keeps the key away from the Harness and ordinary OpenShell reads, not from OpenShell administrators or OCC itself. With the selected gateway:
- Covered. Provider reads and writes return
REDACTEDvalues. Only the Sandbox's own supervisor can fetch provider environments or exchange tokens. The separate operator-mTLS export RPC can return runtime credentials to OCC; it cannot return refresh material. OpenShell withholds a static key that has no credential binding. A Sandbox policy cannot add acredential_bindingfor a profile that defines endpoints, so changing a Sandbox policy cannot moveOPENAI_API_KEYoff the endpoint selected by the source. - Commands the Harness runs. OpenShell matches
binariesagainst the calling process and its ancestors. A command Codex starts that reads the placeholder from an ancestor's environment gets the key inserted at the source's endpoint. It cannot read the key or use it elsewhere; other hosts answer403 credential_endpoint_mismatch. Running Agent tools in a separate Sandbox would remove this use. - Not covered. A Platform Admin can create, attach, and exec in Sandboxes in
any Workspace. A Workspace admin, which includes OCC's gateway principal, can
update a source's profile to add hosts or binaries while Sandboxes use it,
so anyone holding the Backend credential can redirect the key. With
allow_unauthenticated_usersenabled, every caller that reaches the gateway is a Platform Admin; never enable it outside disposable development. The OCC worker can read the source's Kubernetes Secret directly.
- A running Agent uses an updated static value only after its next deployment.
- The OAuth2 types support the client-credentials and refresh-token grants only. Google service accounts, AWS STS, token exchange, and other OpenShell source types remain unavailable.
- After an update of an OAuth2 source's material, running Agents lose its token within one Sandbox provider poll (10 seconds by default) and receive none until they are redeployed.
- OCC does not perform the initial OAuth2 sign-in for a refresh-token source; supply the refresh token from a completed sign-in in a Secret.
- Only one OpenShell Backend can be configured.
- While the Namespace's OpenShell gateway is down, running Codex Agents cannot
serve turns, because their route runs through it. Registration returns
503and deployments wait until it is back.
The OpenShell real Sandbox suite checks model and tool-source delivery, credential routing, untrusted upstreams, outages, and withdrawal through the API and a dedicated Codex Agent. That page records proof limits. The Sandbox Driver startup integration covers Backend membership and selection rules.
| Symptom or message | Cause and fix |
|---|---|
drivers.credential_gateway requires an owning backend entry with type openshell. |
Add the openshell Backend. |
backend[…].drivers.credential_gateway must match … |
Make the Backend member IDs match the selected Driver IDs. |
OpenShell Credential Gateway binaries must be a nonempty list of absolute paths. |
Correct binaries. |
Registering, updating, or deploying bearer-token returns 409: the gateway does not offer it |
Configure toolBinaries. |
Registering, updating, or deploying an OAuth2 type returns 409: the gateway does not offer it |
Configure toolBinaries and select drivers.credential_refresh on the same Backend. |
OAuth2 registration returns 503 with oauth_token_endpoint_unavailable |
Let the gateway Pod reach token_url, and mount a bundle with the issuer's CA over its SSL_CERT_FILE. |
| A tool request reaches the endpoint with the placeholder, or OpenShell denies it | Call it from a toolBinaries executable, at the source's exact host, port, and path. |
Deployment fails with CREDENTIAL_SOURCE_ENVIRONMENT_CONFLICT |
Two sources share a variable. Bind one openai source; give each bearer-token a distinct env_var. |
Model requests fail with 403 "A credential placeholder in the request body cannot be forwarded" |
A tool printed a placeholder into the conversation. Start a new thread, and avoid printing credential variables. |
Registration returns 503 |
Check that the API reaches the gateway, the token file is mounted, and the Workspace exists. |
Registration returns 404 for a name conflict |
A provider named for this source exists without OCC's labels. Remove it in OpenShell, then retry. |
The Agent revision stays inactive with a failed or withheld attachment |
Check the provider in OpenShell and the Sandbox's GetSandboxProviderStatus reason. |
Deployment fails with RUNTIME_MODEL_PROBE_FAILED or RUNTIME_AUTHENTICATION_FAILED |
Confirm binaries names the selected Harness executable (Codex or Node), the Harness trusts the Sandbox CA, and the credential is valid. |