OpenClaw EnterpriseDOCSGitHub

Repository credentials

Repository bindings grant bounded Git HTTPS and GitHub API access. OCC freezes grants into a revision; the worker prepares material. The credential service retains App keys, JWTs and installation tokens. Agents receive gateway bearers, client configuration and CA trust. Start with the operator guide.

Kubernetes supports embedded OpenClaw (api_key) or dedicated Codex (API key or ChatGPT service account), without a Sandbox Driver. Other combinations reject repository-bearing revisions. Helm's Recreate strategy prevents overlapping worker/credential-service owners.

Only the consumer receives repository and model credentials. Dedicated Slack tokens stay in the gateway. Repository profiles and model authentication are independent. Kubernetes policies allow consumer access to the credential sidecar.

For repository-bound Codex consumers, Compute permits the exact broker hostname with allow_local_binding = true and mode = "full". This disables Codex's additional private-address guard and permits every HTTP method at otherwise allowed destinations. Explicit denies, NetworkPolicy, TLS and broker authorization still apply. Unbound Agents retain their policy; see the networking contract.

The separate service owns protected configuration and private sender callbacks. Its controls are open, status, close, and shutdown. Separate Git/gh artifacts exclude signing and service modules. SIGTERM and SIGINT start bounded cleanup.

Repo Driver contract

The optional repo capability uses RepoDriver extends Driver, with the bundled GitHubRepoDriver. Trusted Installation drivers.repo and GitHub Backend drivers.repo select the same configured Driver ID. The shared contract exposes these operations:

Public status contains only sessionId, state, deadlineWallMs and binding (providerInstanceId, repositoryId, grantId). Each response is an immutable snapshot after complete private validation. Cleanup counters and configuration decoding remain private. Status cannot regenerate the closed-schema Git/gh files.

maintenanceIntervalMs schedules worker reconciliation and retries of incomplete repository cleanup; it is not a measured withdrawal bound. Configured IDs, AgentRevision.repositoryCredentials and persisted admitted_spec.repository_credentials retain their meaning.

State derives immutable Driver, Backend, profile and grant context from the admitted revision. It retains original Namespace, Agent, revision, admission and session identities and deadlines after Agent deletion, without bearers or tokens.

Agent deletion retires Compute without waiting for repository-session cleanup. Pending, missing, unknown and invalidated sessions block neither deletion admission nor completion. Retained attempts and cleanup Work survive deletion; cleanup continues independently. New requests share Work by revision and purpose. Deletion, invalidation and elapsed deadlines do not prove disposal or provider revocation. Evidence pruning and durable token recovery are unimplemented.

Worker restart can retain surviving sessions and Compute material. A broker can recover the original broker's committed DISPOSED observation; active or uncertain sessions without one remain unknown. Missing exposed sessions fail the revision and retain cleanup; closing sessions block replacement until disposal. A new authorized revision neither settles old cleanup nor replays Git/API mutations.

Configuration

Canonical platform registry

The GitHub Backend selects configuration.registryPath and names its Driver in drivers.repo. API, worker and service load the same immutable, versioned ConfigMap. The registry contains nonsecret identity and Namespace policy for one App installation and multiple repositories:

json
{  "version": 1,  "backendId": "repository-backend",  "providerInstanceId": "github-production",  "appId": "123456",  "githubInstallationId": "789012",  "maximumDurationSeconds": 86400,  "repositories": [    {      "repositoryRef": "application",      "repositoryId": "345678",      "repository": "example/project",      "namespaces": [{ "namespaceId": "team", "profiles": ["git-read", "git-write", "git-full"] }]    }  ]}

Use platform Namespace IDs. App, installation and repository IDs are positive decimal safe integers as strings. Repository names are canonicalized to lowercase. The registry admits at most 1,000 repositories, 128 Namespace policies per repository and 4,096 overall. References, numeric IDs and canonical names must be unique.

The grant fingerprint covers provider, App, installation, repository, maximum duration, Namespace, allowed profiles, optional push-ref policy, selected profile and permission contract. The service independently compares it before admission; a retained reference cannot preserve a grant after policy changes.

Each Namespace policy may set an optional pushRefAllowlist to prevent accidental native Git pushes outside selected branches. This is not server-side branch authorization.

Driver configuration supplies controlSocket, sessionDurationSeconds and publicCaPath, never the App key. See Backend configuration and the installation procedure.

Repository options

GET /namespaces/:namespaceId/agents/repository-options requires Agent create; the exact-Agent editing route requires update. Both return approved repositoryRef, displayName, allowedProfiles and optional description. descriptionRefs accepts up to 20 unique, comma-separated refs; meta.descriptionsPending signals background work, and missing descriptions never block selection. Authorized discovery failure yields 503 REPOSITORY_OPTIONS_UNAVAILABLE; no approvals yields []; a closed Namespace yields 409. Only success or that outage permits a fresh ordinary draft; editing requires success. Writes reauthorize and resolve.

The service rechecks approved refs and fetches metadata with a private, repository-scoped Metadata-read token, validating GitHub's numeric repository ID; the Driver checks provider, App, installation and repository IDs. Lookups share provider capacity and token cleanup with sessions and are cached for five minutes. Descriptions never grant access.

Profiles

The Console offers Read-only (git-read) and Contributor (git-full). Contributor includes pushes, PRs and issue management; Customize access can disable issue management (git-write). Push and PR access stay bundled. Direct bindings default to git-write; repositoryAccess records inheritance. All profiles include GitHub API access.

The access-level reference defines the exact permissions, supported commands and GraphQL boundary. Every session selects one repository. Writable levels are not a promise that an Agent cannot merge: GitHub rules still govern protected branches. Administration, workflow editing, Actions control and secrets permissions are not requested. Missing App permissions fail without widening the grant.

Standalone service inputs

A protected JSON file supplies gateway, sessionPolicy, backend and optional positive safe-integer limits. The service validates configuration before listening. Configuration and private keys must be regular files owned by root or the service user, with private permissions. Every directory ancestor must have one of those owners and reject group/other writes. A root-owned sticky ancestor such as /tmp is allowed above the immediate parent; the immediate parent must always reject group/other writes. Symlinks and file replacement during loading are rejected. See the configuration flow for validation and key ownership. For standalone single-repository operation:

json
{  "gateway": {    "publicOrigin": "https://credentials.example.internal",    "listen": "0.0.0.0:8443",    "tlsCertFile": "/run/repository-credentials/tls.crt",    "tlsKeyFile": "/run/repository-credentials/tls.key",    "controlSocket": "/run/repository-control/control.sock"  },  "sessionPolicy": {    "maximumDurationSeconds": 172800,    "defaultProfile": "git-write",    "allowedProfiles": ["git-read", "git-write", "git-full"]  },  "backend": {    "kind": "github-app",    "providerInstanceId": "github-production",    "configVersion": "1",    "appId": "123456",    "installationId": "789012",    "repositoryId": "345678",    "repository": "example/project",    "privateKeyFile": "/run/repository-credentials/app.pem"  }}

The identifiers are examples. Production upstream origins are fixed to github.com and api.github.com. Registry mode instead uses backend fields kind: "github-app-registry", backendId, registryFile and privateKeyFile; all repository policy comes from that registry, and unbound admission is refused.

Kubernetes composition copies selected projection generations into service-owned private files before protected-path validation. API and worker receive registry/public CA inputs; only the service receives App and TLS private keys.

The privileged GitHub transport captures installation, repository and permission profile at construction. Its operations issue scoped tokens and revoke owned tokens; callers cannot supply HTTP requests. Extending those operations changes a credential boundary and requires security review.

The service image must trust GitHub's HTTPS certificate chain. For a private CA, supply a readable bundle and NODE_EXTRA_CA_CERTS; keep certificate and hostname verification enabled.

Git discovery, upload-pack and receive-pack accept case differences in the admitted owner/repository and an optional .git suffix. The backend constructs a canonical upstream path; a literal .git repository name remains part of the admitted identity. Endpoint names, methods, media types, service queries and profile restrictions still apply. API request paths and repository authority remain unchanged.

Sessions and closure

The trusted worker or local operator uses HTTP over a private mode-0600 Unix socket:

Request Response
GET /v1/capabilities Durable admission version for the registry-backed service
POST /v1/sessions with durationSeconds and optional profile Private status, client configuration and bearer once
GET /v1/sessions/{id} Private session and cleanup status
POST /v1/sessions/{id}/close Immediate local closure status; cleanup reported separately

The socket's parent is private to the service/operator. It is never mounted into the client. Control bodies are limited to 16 KiB. The HTTPS client listener has no admission or close endpoint.

X-Admission-Id combines a 13-digit Unix-millisecond timestamp, hyphen and lowercase UUIDv4. The CLI prints this nonsecret ID before dispatch. HTTP 201 returns the bearer once; matching ID/duration/profile returns HTTP 200 with status only. Conflicts fail. Follow lost-response recovery before explicitly requesting replacement material.

Registry-backed brokers advertise durableAdmissionVersion: 1 privately. The worker checks it before new attempts and readiness; an unavailable or older broker blocks both. This does not prove journal availability, absence or disposal. Recovery, closure and runtime retirement do not depend on the check.

Platform admission binds namespaceId, repositoryRef, normalized profile, expectedBinding and deadlineWallMs to the persisted attempt. The worker's private receipt socket commits a reservation before the broker releases a bearer. A recovery-only lookup can durably fence a missing admission; a reservation or active session without a confirmed terminal receipt remains unknown. Only the original broker can record its exact DISPOSED result. Receipts contain no credentials and remain with attempt history. Journal failure prevents new bound admission and cannot establish absence or disposal.

Unseen IDs must be less than 60 seconds old and never future-dated. Process-local correlations are bounded to twice the session limit and can return overloaded; unresolved cleanup survives that window. Standalone correlations do not survive restart. No correlation recovers a bearer.

Session duration is independent of token lifetime. Replacement uses the original grant and must cover the remaining exchange budget plus safety margin. Idle sessions need no periodic mint. The bearer works while its session process and upstream authorization survive.

Authentication eligibility and terminal cleanup expiry are separate deadlines. Both use elapsed monotonic time from the original capture; delayed acquisition settlement cannot extend either. The GitHub adapter allows 60 seconds of provider clock skew and conservatively stops authentication before the reported expiry. Cleanup retains the one-hour bound from local receipt. A forward wall-clock change can deny authentication but cannot establish remote expiration.

Closing or expiring a session prevents new use immediately and cancels owned exchanges. CLOSED does not imply confirmed revocation. Private control status distinguishes pending, revoked, expired and uncertain credentials, plus auxiliary cleanup. DISPOSED requires settled actions, resolved access-token obligations and completed auxiliary finalization; historical revoked/expired counters may remain nonzero. An uncertain issuance blocks automatic minting. An uncertain push or API mutation is never automatically replayed.

Failed admission can also retain cleanup work. If session construction fails, renewal access closes immediately; retained material remains counted against session capacity and shutdown's pendingAuxiliary until admitted callbacks finish and their material is disposed.

GitHub response data

Bounded REST JSON responses omit the provider's temp_clone_token from the repository object, its parent and source repository relationships, and pull-request head.repo and base.repo objects. Human text and unrelated metadata remain unchanged. Qualified machine links still pass through the existing origin, repository, route, and profile checks before gateway rewriting; other informational links remain data.

Client routing and limits

Ordinary Git uses /usr/bin/git and native configuration. Git owns commands, identity, hooks, aliases, remotes, push URLs, worktrees, and user settings. The client does not parse Git arguments or create a temporary HOME.

For each generation, native preparation validates public manifest/session metadata, identities, paths and file custody, then writes private aggregate gitconfig. Kubernetes invokes it through the private subPath after material copying; both init steps gate startup. It neither reads bearers nor admits sessions. Each canonical HTTPS host maps to one gateway origin; conflicting origins fail preparation. Pins cannot change an already-chosen connection origin. Same-origin public CA inputs must agree.

A host-prefix rewrite preserves owner/repository casing and an optional terminal .git. It also routes unadmitted repositories on that host to the gateway, where the helper releases no bearer. Separately authenticated same-host use needs an explicit native configuration override. SSH and additional-repository submodule workflows are outside supported acceptance.

The generated defaults scope helper reset, credential.useHttpPath=true, verified TLS, optional CA trust and disabled redirects to the exact gateway HTTPS origin. The helper checks effective protocol, host/port, username and repository path; escaped paths, dot segments, extra components and unmatched names receive no bearer. A literal name ending in .git can overlap another admitted identity; the helper compares both spellings and refuses ambiguity or alternate grants.

Duplicate repository bindings remain valid. Select one with OCE_REPOSITORY_REF; gh also propagates OCE_REPOSITORY_SELECTION containing generation, repository reference and session ID. Conflicting or stale pins fail remote authentication. The helper command embeds the prepared generation and refuses material from a new generation. It validates the selected original deadline before reading its bearer; gateway closure can deny use earlier. Local commands continue after expiry or with stale pins because they do not consult the helper. Generation pinning does not promise a command-wide snapshot across arbitrary subprocesses.

Native configuration can override these defaults, and additional helpers or stores can retain credentials. The installed helper's store and erase are inert. There is no whole-command preflight, and the client never retries an uncertain mutation to obtain success.

The API launcher requires GitHub CLI 2.100.0, GH_HOST=github.com, a gateway hostname with verified TLS, and HTTPS port 443. Its private hosts.yml uses the experimental api_host routing option and stores only the gateway bearer in oauth_token. Supported API calls use relative endpoint paths. The launcher admits the selected read and contribution commands; browser flows, extensions, absolute API destinations and arbitrary command compatibility are excluded. Response rewriting is limited to validated pagination links and explicitly followed resource fields. Native /repositories/<id> response URLs must match the configured repository ID and are rewritten to its admitted /repos/OWNER/REPO route. Issue collection pagination accepts bounded after and before cursors; direct requests to repository-ID routes remain unsupported. Informational labels, milestones, nested repository metadata and human-authored content remain unchanged. Each listener and sender captures its permitted upstream origins at construction. The sender rejects ambiguous or malformed adapter headers before dispatch, then supplies canonical authority, framing and connection headers within the configured header bounds. Routing configuration is not network egress confinement.

Default service bounds are 16 sessions including pending cleanup, two credential slots per session, one provider action and 64 queued actions, 64 sockets per listener, and 32 exchanges total and four per session. Headers are limited to 32 KiB/64 pairs; request targets to 8 KiB. Git fetch input is 1 MiB; push input and Git output are 256 MiB. API input is 1 MiB and response data 8 MiB. Git gzip input has independent wire and decoded limits. Exchanges have a five-minute total bound and 60-second credential margin. HTTPS client header timing begins on the TLS socket after the handshake and ends when an authenticated request reserves exchange capacity; the exchange deadline bounds acquisition and forwarding. The upstream response-header deadline starts after upload finishes, unless the response headers already arrived. Connection, input and stall deadlines remain independent. Provider actions have at most 30 seconds. Shutdown allows 60 seconds for cleanup before reporting unresolved obligations and terminating. Unsettled actions retain capacity until exit; grace expiry does not establish DISPOSED or confirmed revocation. Restart cannot recover the lost provider cleanup inventory. Overrides remain positive and finite.

The testing guide separates source, artifact, container and live-provider proof. Local fixtures establish neither live GitHub compatibility nor release readiness. See the service flow and Agent flow for implementation ownership.

Search documentation