OpenClaw EnterpriseDOCSGitHub

Use a static credential source on the local OpenShell profile

This is the static Credential Gateway setup for new and existing sources. It does not qualify protected Credential Proxy access or transfer source ownership. For protected setup and upgrade decisions, start with Credential Proxy setup.

Use this procedure to register an OpenAI-compatible API key with the OpenShell Credential Gateway and bind it to a dedicated Codex Agent on the local OpenShell profile. OpenShell keeps its own copy of the key, including with a Git Proxy selected, and substitutes it only on requests to its authorized endpoint; the Harness receives only a placeholder. By default the endpoint is https://api.openai.com/v1. The credential source reference defines the API behavior.

The profile installs private Gateway routing, so the deployment provisions an OpenShell Sandbox for the dedicated Codex Harness. After deployment, verify a model response.

For native Git with worker-managed GitHub App credentials, use the OpenShell Git push guide.

Before you start

Start the Kubernetes-only OpenShell profile and build the CLI:

bash
go build -trimpath -o bin/occ ./cmd/occexport OCC_DEVELOPMENT_COMPUTE_DRIVER=kubernetesexport OCC_DEVELOPMENT_CONTROL_PLANE=kubernetesexport OCC_DEVELOPMENT_SANDBOX_DRIVER=openshell./scripts/dev-up

Export the API URL and service-key file that dev-up printed, then select the bootstrap Namespace:

bash
export OCC_URL='<API URL printed by scripts/dev-up>'export OCC_SERVICE_KEY_FILE='<Service key file printed by scripts/dev-up>'export OCC_NAMESPACE="$(./bin/occ namespace list -o json |  jq -r '.[] | select(.name == "default") | .id')"

You need an existing provider API key in a private file, jq, and a model name that key can use. Register the complete native model ID under models.providers.codex, including any namespace. OCE's generated Gateway configuration supplies the native provider explicitly; do not add it to the model ID yourself.

Register the key

If arriving from Credential Proxy setup, use only the openai example below, obtain whole-execution consent before creation, then return to live-source setup. Registration creates a Gateway copy. Live-source setup requires explicit adoption; the copied-key rotation and cleanup below do not describe the adopted path.

Store the key as a Namespace Secret. Build the request file with owner-only permissions, delete it after use, and never put the key on the command line:

bash
(umask 077; tr -d '\n' < /path/to/openai-key |  jq -Rs '{name: "openai-model-key", value: .}' > model-secret.json)SECRET_REF="$(./bin/occ secret create --file model-secret.json -o json | jq -c .ref)"rm model-secret.json

For a Codex personal access token, including a service-account token, save that token as the Secret above and register type: "codex-pat" with secrets: {access_token: $ref} in the command below. Select dedicated Codex; OpenShell keeps the real token outside the Harness. The Driver reference defines its permitted endpoints and authentication limits.

Register an API-key Secret as an openai credential source:

Choose the revision model provider through an Installation default or a deployment override. For a custom endpoint, set MODEL_AUTHORIZED_BASE_URL to that URL when registering the source. Registration authorizes the destination; it does not select the Agent's route. Keep models.providers.codex.baseUrl at the fail-closed http://127.0.0.1:9 shown below for the Gateway's transport bridge. The current source profile uses Bearer authorization; an endpoint requiring x-api-key is not supported by this profile.

bash
jq -n --argjson ref "$SECRET_REF" --arg authorized_base_url "${MODEL_AUTHORIZED_BASE_URL:-}" \  '{name: "openai", type: "openai", config: (if $authorized_base_url == "" then {} else {authorized_base_url: $authorized_base_url} end), secrets: {api_key: $ref}}' > credential-source.jsonSOURCE_ID="$(./bin/occ credential-source create --file credential-source.json -o json |  jq -r .id)"./bin/occ credential-source get "$SOURCE_ID"

Expected result: STATE and GATEWAY STATUS both show ready. The response never contains the key. Changing the Secret afterward does not change the Gateway copy. Explicit live adoption follows the separate procedure linked above.

Create the Agent and grant the source

Write a dedicated Codex Configuration. Replace each <model> with the native model ID, that your provider account can use:

bash
cat > configuration.json <<'JSON'{  "kind": "agent",  "values": {    "gateway": {      "mode": "local",      "bind": "lan",      "controlUi": { "enabled": false },      "auth": {        "password": { "source": "env", "provider": "default", "id": "OPENCLAW_GATEWAY_PASSWORD" }      },      "http": { "endpoints": { "chatCompletions": { "enabled": true } } }    },    "agents": {      "defaults": {        "model": "codex/<model>",        "models": { "codex/<model>": { "agentRuntime": { "id": "codex" } } }      }    },    "models": {      "providers": {        "codex": {          "baseUrl": "http://127.0.0.1:9",          "api": "openai-responses",          "models": [{ "id": "<model>", "name": "<model>" }]        }      }    },    "plugins": {      "allow": ["codex"],      "entries": {        "codex": {          "enabled": true,          "config": {            "appServer": {              "mode": "guardian",              "approvalPolicy": "on-request",              "sandbox": "read-only",              "transport": "websocket",              "url": "${APP_SERVER_URL}",              "authToken": "${APP_SERVER_TOKEN}"            }          }        }      }    }  }}JSONCONFIGURATION_ID="$(./bin/occ configuration create --file configuration.json -o json | jq -r .id)"

The OpenShell Sandbox Driver freezes "sandbox": "danger-full-access" and "approvalsReviewer": "user" for dedicated Codex versions and removes appServer.networkProxy. approvalPolicy stays configured. The bridge selects the native :danger-full-access profile, which the Codex account must allow. OpenShell enforces the outer sandbox; restricted native profiles require nested bubblewrap namespaces that it blocks. See OpenShell Sandbox configuration.

Create the Agent with the source as its Harness authentication:

bash
jq -n --arg configuration "$CONFIGURATION_ID" --arg source "$SOURCE_ID" \  '{name: "openshell-codex", configurationId: $configuration, executionMode: "dedicated",    harnessAuth: {method: "credential_source", sourceId: $source},    credentialSources: [{sourceId: $source}]}' > agent.json./bin/occ agent create --file agent.json -o json > agent-response.jsonAGENT_ID="$(jq -r .id agent-response.json)"

Deployment requires the Agent's service principal to have operate on the exact source. It needs no permission on the Secret:

bash
jq -n '{name: "Use a credential source",  permissions: [{action: "operate", resourceKind: "credential_source"}]}' > role.jsonROLE_ID="$(./bin/occ iam role create --file role.json -o json | jq -r .id)"jq -n --arg principal "$(jq -r .servicePrincipalId agent-response.json)" \  --arg role "$ROLE_ID" --arg source "$SOURCE_ID" \  '{subjectKind: "identity", subjectId: $principal, roleId: $role,    resourceKind: "credential_source", resourceId: $source}' > binding.json./bin/occ iam access-binding create --file binding.json

Deploy and check the result

bash
./bin/occ agent runtime-credentials provision "$AGENT_ID"DEPLOYMENT_ID="$(./bin/occ agent deploy "$AGENT_ID" -o json | jq -r .id)"./bin/occ agent deployment-status "$AGENT_ID" "$DEPLOYMENT_ID"

Expected result: OCC accepts the deployment and freezes {"method": "credential_source", "sourceId": "cs_…"} in the version. Compute starts the Agent Gateway, then OpenShell creates the Sandbox, and the version becomes active after Harness readiness and workspace-node connection. For codex-pat, readiness also requires PAT authentication in the serving process; a successful earlier login check is insufficient. Startup can take several minutes. Without the access binding, the deploy request fails with 403. After activation, verify a real model response; readiness does not prove inference.

If a managed codex-pat deployment stays unready, verify the Agent binds the intended source, retains its required access binding, and can reach the permitted provider endpoints through OpenShell. Use Harness troubleshooting and the deployment status above to inspect the failure. A serving process that started without PAT authentication can remain unauthenticated after the provider recovers; status polling observes authentication but does not reload it. Absent authentication alone does not establish token revocation.

Let the existing deployment reach a terminal status before retrying; readiness waits have a convergence deadline, and pending cleanup must finish. After correcting the cause of a failed deployment, run ./bin/occ agent deploy "$AGENT_ID" again to create a fresh version, then check its deployment status and verify a model response.

Rotate the key

Changing the Secret does not change the gateway's copy. Update the Secret, or create a replacement, and then push it to the gateway:

bash
./bin/occ credential-source update "$SOURCE_ID"

For a replacement Secret, add --file with {"secrets": {"api_key": <replacement ref>}} for openai, or {"secrets": {"access_token": <replacement ref>}} for codex-pat. Running Agents keep the old value until they are redeployed. The update reference lists the required permissions and failure cases.

Clean up

A source cannot be deleted while an Agent references it. Record the source's current Secret, delete the Agent, and wait until agent get returns 404:

bash
SECRET_ID="$(./bin/occ credential-source get "$SOURCE_ID" -o json | jq -er '  if .type == "openai" then .secrets.api_key.id  elif .type == "codex-pat" then .secrets.access_token.id  else error("Expected an openai or codex-pat source") end')"./bin/occ agent delete "$AGENT_ID"./bin/occ agent get "$AGENT_ID"

Then delete the source, its Secret, the Configuration, and the Role:

bash
./bin/occ credential-source delete "$SOURCE_ID"./bin/occ secret delete "$SECRET_ID"./bin/occ configuration delete "$CONFIGURATION_ID"./bin/occ iam role delete "$ROLE_ID"rm -f credential-source.json configuration.json agent.json agent-response.json role.json binding.json

Deleting the source also removes the gateway's copy. Within about a minute of registration, credential-source delete returns 503 and keeps the source as deleting; run it again after that window. To discard the whole environment, stop and clean up the profile instead.

Troubleshoot

Search documentation