OpenClaw EnterpriseDOCSGitHub

Agent repository credential flow

Overview

Codex uses prepared sessions; OpenShell receives placeholders. Workers authorize signed requests against live grants. Enrollment; qualification.

Entry Points

Flow

graph TD
  Console["<b>Console form</b><br/>Load Namespace choices"] --> Options["<b>Repository policy</b><br/>Refs, names, profiles"]
  Options --> API["<b>Agent API</b><br/>Recheck and save refs"]
  Options -->|Unverified authorization or discovery error| CreateBlocked["<b>Create blocked</b><br/>Retry before writes"]
  API -->|Known zero-binding rejection| OrdinaryRetry["<b>Ordinary retry</b><br/>Reuse Configuration"]
  OrdinaryRetry --> API
  API -->|Known repository-scoped rejection| FormLocked["<b>Recovery locked</b><br/>Keep Configuration ID; block retry"]
  FormLocked -->|Reload choices| Options
  Options -->|Successful reload| Reselect["<b>Reselect scope</b><br/>At least one repo and profile"]
  Reselect -->|Explicit selection| API
  Options -->|Reload denied, conflict or unavailable| FormLocked
  Options -->|Expired or obsolete view| ViewStop["<b>No current-view retry</b><br/>Sign in or ignore stale completion"]
  API --> Version["<b>Deploy</b><br/>Freeze grants and deadline"]
  Version --> Worker["<b>Claimed worker</b><br/>Recheck actor and policy"]
  Worker -->|Denied or expired| Close["<b>Cleanup</b><br/>Close exact attempts"]
  Worker -->|Native sender| Capability["<b>Check capability</b><br/>Fresh admissions only"]
  Worker -->|Protected Driver| Protected["<b>OCC finite session</b><br/>Prepare and install through Driver"]
  Protected --> Compute
  Worker -->|Active standing Git evidence missing| Observe["<b>Observe existing Harness</b><br/>No preparation or issuance"]
  Observe -->|Durable check within five seconds| Worker
  Worker -->|Repository execution cannot resume| Withdraw["<b>Withdraw Git</b><br/>New revision required"]
  Withdraw --> Close
  Capability -->|Available| Attempt["<b>Persist opening</b><br/>Before control request"]
  Capability -->|Unavailable| Blocked["<b>Block new admission</b><br/>Worker stays unready"]
  Attempt --> Service["<b>Private control</b><br/>Check bound registry grant"]
  Service --> Receipt["<b>Journal</b><br/>Commit exact admission"]
  Receipt -->|Acknowledged| New["<b>New material</b><br/>Record ID before delivery"]
  Service -->|Open session| Retained["<b>Retained material</b><br/>No bearer recovery"]
  Service -->|Lost response| Recover["<b>Recover only</b><br/>Find or fence, then close"]
  Recover -->|Never delivered or disposed| Attempt
  Recover -->|Known closing session| Wait["<b>Wait for disposal</b><br/>Retry, no remint"]
  Wait --> Worker
  Recover -->|Known session missing| Refuse["<b>Fail version</b><br/>Retain cleanup obligation"]
  New --> Compute["<b>Compute</b><br/>Validate complete set"]
  Retained --> Compute
  Compute -->|Missing retained files| Repair["<b>Repair exact subset</b><br/>Close and verify disposal"]
  Repair -->|Disposed| Compute
  Repair -->|Closing| Wait
  Repair -->|Missing| Refuse
  Refuse --> Close
  Compute --> Pod["<b>Consumer</b><br/>Matching Agent revision ID and material generation"]
  Pod --> Command["<b>Git or gh command</b><br/>Pin target and session"]
  Command -->|Other requests| Gateway["<b>HTTPS gateway</b><br/>Exact repository/profile"]
  Command -->|Managed push with policy| PushRefs["<b>Pre-push</b><br/>Check destination refs"]
  PushRefs -->|Allowed| Gateway
  PushRefs -->|Denied| PushDenied["<b>Reject whole push</b><br/>No ref update"]
  Pod -->|Durable maintenance| Worker
  Pod -->|Stop or retire| Close
  Close -->|Unavailable or pending| Queue["<b>Durable cleanup</b><br/>Coalesce by version and purpose"]
  Queue --> Close
  Close -->|Confirmed disposal| Terminal["<b>Receipt</b><br/>Commit exact result"]
  Terminal --> Done["<b>Cleanup settled</b><br/>Retain immutable evidence"]
  Pod -->|Delete Agent| Delete["<b>Delete</b><br/>Queue cleanup and retire Compute"]
  Delete -->|Independent cleanup| Queue
  Delete -->|Runtime retired| Finalize["<b>Finalizer</b><br/>Detach and remove live rows"]

  classDef state fill:#EDF2F7,stroke:#879AB0,color:#25364A,stroke-width:1px
  classDef operation fill:#EBF3F0,stroke:#7F9D93,color:#2B4038,stroke-width:1px
  classDef condition fill:#F7F1E5,stroke:#B3A078,color:#514532,stroke-width:1px
  class API,Version,Attempt,Queue,FormLocked,Receipt,Terminal state
  class Console,Options,Worker,Service,New,Retained,Compute,Pod,Command,Gateway,Close,Done,Delete,Finalize operation
  class Recover,Repair,Refuse,Wait,ViewStop,CreateBlocked,OrdinaryRetry,Reselect,PushRefs,PushDenied,Capability,Blocked condition

Execution Trace

1. Project choices and resolve Namespace policy during Agent admission

listRepositoryOptions checks Agent create in the Namespace or update on the exact Agent, plus Compute availability. Bundled listings use the bound GitHub catalog. Programmatic listings use the registered Repo Driver. Both reject stale owner results. Deploy checks the exact Harness. Empty catalogs return []. Absent repository adds meta.notConfigured. Closed Namespaces conflict. Classified discovery failures return 503 REPOSITORY_OPTIONS_UNAVAILABLE.

OCC allows 1,000 ordinary repositories plus the separate Installation option (1,001 choices). OCC rejects overflow with 503 DEPENDENCY_UNAVAILABLE. createRepositoryFields searches choices and caps attachments at 16. repositorySettings enforces bound Installation policy for explicit and inherited saves. Unbound saves use the registered Repo Driver. The database checks bindings. Only fresh, empty drafts tolerate classified outages. Catalogs omit sender metadata, credentials and provider descriptions.

Save Configuration first. Known Agent rejection permits reuse. Repository-scoped retries require a successful reload and explicit selection of at least one repository and profile. Read stored Agent and Configuration after unknown outcomes.

Requests carry refs and optional profiles, never tokens or grant IDs. Nonempty selections without bound built-in policy or a selected Repo Driver return 409 RESOURCE_CONFLICT. OCC preserves order. GitHub defaults legacy profiles to git-write without provider or control-socket calls.

apps/controller/src/drivers/repo/github/access/registry.ts:resolveGitHubRepositoryBinding requires an exact Namespace/ref/profile. Each binding gets one grant with a fingerprint covering provider, App, installation, repository, duration, full Namespace profile policy, exact permissions and optional normalized push-ref allowlist. Omitted selections persist. [] clears them.

2. Freeze a deployable version

apps/controller/src/composition/installation-config.ts:loadInstallationConfiguration matches GitHub Backend and registry IDs. It snapshots policy before loading packages or registries, preserving protected Driver identity. packages/occ/src/index.ts:OpenClawController.forInstallation binds that owner. File-based repository selects built-in policy. Optional repository.policyMode: built-in preserves identity. Startup rejects other modes, drivers.repo and separate public Repo Drivers. Programmatic registration remains available. apps/controller/src/drivers/egress-proxy/openshell-configuration.ts owns OpenShell parsing and chart constraints.

OpenClawController.admitRepositoryCredentials calls resolveBindings for bound policy and RepositoryPolicyDriver.resolve otherwise. OCC checks Compute topology and freezes Driver identity, grants and deadline across renewal and recovery. validateRepositoryBrokerNetworkProxy rejects broker-blocking Codex settings.

After approval lookup, changed bound ownership returns 503 before invocation. Unbound deploys recheck their Driver. Denial or changes to binding or scope block revision creation.

Protected repository.preparedGitHub uses the installed Sandbox/Credential Proxy Driver. Finite sessions need no native sender, control socket, broker CA or session files.

repository.nativeSender selects GitHubNativeRepositorySender to own native sessions and files without an App key. The worker's prepared provider issues and holds credentials. Cleanup uses recorded identity without policy resolution or reauthorization.

3. Record ownership before opening a session

apps/controller/src/worker/repository-credentials.ts:RepositoryCredentialLifecycle.prepare rechecks actor, Namespace, Agent, revision, Driver, grant and deadline. GitHubNativeRepositorySender.checkAdmissionReady checks broker capability for fresh admission with a two-second timeout. Fresh OPEN sends preparedOnly: true. Old parsers reject it after socket replacement. Control requires a prepared backend. Before Driver invocation, State uses a live claim and Namespace/Agent locks to reject stopped or deleting owners and save immutable cleanup context. State keeps identifiers and phases, never bearers or files.

apps/controller/src/backends/repository-credentials/control-client.ts:UnixRepositoryCredentialControlClient sends the bound request over the private socket. apps/controller/src/drivers/repo/github/access/warm-registry-factory.ts:createPreparedGitHubRegistryFactory resolves and compares the grant. The client accepts DISPOSED with historical revoked or expired counts and zero outstanding obligations. apps/controller/src/drivers/repo/github/access/client/config.ts:encodeRepositoryCredentialSessionFiles encodes files. The worker records the session ID before delivery to Compute.

Confirmed open sessions yield retained bindings without files. recoverOnly finds or fences unfinished admissions and closes recovered sessions. Fresh material requires confirmed disposal or a missing opening with no recorded session. Invalidation blocks replacement. Closing raises REPOSITORY_CLEANUP_PENDING within Work bounds and the revision deadline. Closing cleanup uses the configured interval. Other obligations retain their code and retry at age / 40, bounded by that interval and ten minutes.

apps/controller/src/drivers/repo/access/control.ts:createControlAdmission reserves before release. apps/controller/src/backends/repository-credentials/receipt-store.ts:RepositoryReceiptStore retains fences and original-broker disposal evidence after pruning. Transport failure cannot establish absence. preparedOnly requires protocol 3. Unmarked v1 requests can only recover and close original IDs. RepositoryCredentialLifecycle.verifyRelease and apps/controller/src/drivers/repo/github/access/prepared-repository-git.ts:sessionQuery require protocol 3 for native release and material selection. Protected sessions use protocol 2. Selection checks policy-reduction version 2 against execution admission v7, the exact installed plan, recipient, standing approval and session lifetime.

apps/controller/src/drivers/repo/access/protected-repository-git-installation.ts:selectedRepositoryGrantSql shares per-member selection, Driver and expiry checks between production and withdrawal. Frozen identities reject session replacement. Transaction locks remain.

4. Deliver and retain one complete runtime generation

apps/controller/src/drivers/compute/kubernetes/repository-material.ts:repositoryMaterialSpec validates new | retained bindings against the version and hashes sorted reference/session pairs. apps/controller/src/drivers/compute/kubernetes/repository-material-store.ts:RepositoryMaterialStore.prepare checks ownership, contents and deadlines before creating immutable Agent/version-owned Secrets. It rechecks deadlines after requests and before creation or return. Expiry leaves Secrets for owned cleanup. RepositoryCredentialLifecycle.repair closes missing retained bindings and retries Compute once after confirmed disposal. Missing inventory fails.

apps/controller/src/drivers/compute/kubernetes/repository-material.ts:repositoryMaterialDeployment mounts Secrets only in the first initializer. Sorted projections prevent key-order rollouts. Session replacement still rolls. apps/controller/src/drivers/compute/kubernetes/repository-material-init.ts:REPOSITORY_MATERIAL_INIT_ENTRYPOINT validates the full projection before writing in-memory mode-0700 directories and mode-0600 files. REPOSITORY_NATIVE_GIT_INIT_ENTRYPOINT mounts the private subPath at /run/oce/repository-credentials outside the fsGroup-writable root and calls apps/controller/src/drivers/repo/github/access/client/native-git.ts:prepareNativeGitConfiguration to check private files and write gitconfig. Retry removes only validated private gitconfig. Both initializers gate startup. The consumer mount is read-only. Metadata and gateway bearers stay separate.

apps/controller/src/drivers/compute/kubernetes/index.ts:KubernetesComputeDriver.activateRevision replaces consumers whenever material changes, including within a version. Readiness requires role, version, generation and current deadlines. Dedicated replacement preserves enrollment and version-private storage. KubernetesComputeDriver.prepareRevision rechecks material after plugin, gateway and node observations; generation changes, lost readiness or expiry return incomplete. Activation rechecks deadlines after observation. Dedicated gateways lack repository material and repository-gateway egress. Both consumers trust CAs through SSL_CERT_FILE, GIT_SSL_CAINFO and NODE_EXTRA_CA_CERTS; overrides fail. Compute grants consumer egress; Helm admits consumers through credential-sidecar ingress selectors. For prepared native Git, apps/controller/src/drivers/compute/kubernetes/repository-compute-ingress.ts:reconcilePreparedRepositoryIngress requires the peer’s hostname, TLS port and worker selector to match Helm’s broker Service. System Git includes /run/oce/repository-credentials/gitconfig and preserves global settings. Embedded repositoryNativeConfiguration puts the gh router first in tools.exec.pathPrepend. AGENT_RUNTIME_ENTRYPOINT sets Codex's allow_login_shell=false and shell_environment_policy.set.PATH and retains the Pod's NODE_EXTRA_CA_CERTS for sandboxed commands. Model authentication persists. Consumer material excludes App keys, JWTs, installation tokens and the control socket. The networking contract defines Codex plugin and repository-bound filesystem and proxy access, explicit denies and dedicated/embedded eligibility. Unbound policy, broker authorization and TLS verification still apply. Codex checks the named profile at thread start, resume, fork and turns.

5. Authenticate native Git and route GitHub CLI commands

Configuration routes canonical HTTPS hosts to the gateway. Before returning a bearer, the helper checks host, path, generation and deadline. OCE_REPOSITORY_REF selects bindings. Routing limits describe command preflight and egress. pushRefAllowlist selects image-owned hooks. scripts/build-repository-credentials.ts:stage builds launchers that resolve dispatcher URLs through symlinks and relocations. apps/controller/src/drivers/repo/github/access/client/hook-dispatch.ts:checkPush rejects disallowed refs before updates and forwards arguments and input to common-directory hooks. See hook limits.

apps/controller/src/drivers/repo/github/access/client/router.ts:routeRepositoryClient routes gh by target or effective remote, pins generation, ref and session, selects private configuration and preserves HOME and shared selection.

OpenShell's apps/controller/src/drivers/repo/github/access/routes/request-selection.ts:requestHead accepts CLI metadata. git-write draftScope excludes the timezone header. installationApiOperation admits gh auth status API-root reads under current GitHub API grants. The grant owner authorizes signed requests. apps/controller/src/drivers/egress-proxy/openshell-online-git-authority.ts:validBinding uses normalizePushRefAllowlist for Git-only policies, including terminal /*. Draft heads require concrete refs. Receive-pack rejects disallowed refs before approval. Access levels define the grants.

Service exchange checks bearer, fixed repository/profile, capacity and deadlines; token refresh uses that grant until expiry.

6. Maintain, recover and retire ownership

The worker binds ProtectedRepositoryGitAuthority with matching Drivers. OpenShellGitRuntime.start reserves one instance before installing worker dependencies. Failed setup permits retry. Close is terminal. Serial reconcile uses a 25-second timeout and one-second interval. Close aborts and joins. Readback cannot restore authority.

The gRPC client verifies profile identity before treating omitted endpoint/credential lists as empty. Withdrawal accepts files-only profiles, refusing missing, mismatched or Git-overlapping profiles.

Maintenance uses 30 seconds, the sender interval or shorter Compute interval. Restart invalidates missing sessions without reminting. Prepared custody erases standing tokens after verified expiry plus skew; uncertain revocations remain unresolved.

Standing admission requires permission and current scope. Reconciliation retains the bound Installation policy or unbound Repo Driver; conflicts prevent startup. Under Namespace/Agent locks, eligibility rejects withdrawal or scope changes with REPOSITORY_STANDING_POLICY_CHANGED before release. GitHub fingerprints exact permissions and repository identity. Issuance/delivery recheck opt-ins and grant; standing delivery requires one binding. After hook acknowledgement, the observer rechecks renewal policy and recipient. It refuses expiry, unacknowledged launches and observed-only previews. Cold verification and predecessor observation confirm expired launch identity through fresh Sandbox and main-process readback. Freshness limits govern subtraction and succession. Installation and post-detach completion require ready credentials.

ControllerWorker.assertRepositoryGitMaterial awaits committed tokens after activation. Quarantine/timeout fails deployment. For existing Agents, continueRevisionWithoutGit atomically records REPOSITORY_GIT_TOKEN_QUARANTINED or REPOSITORY_GIT_TOKEN_PENDING and maintenance, or retires the version (first-token handling).

Missing standing evidence closes Git. Within five seconds, the worker rechecks model authority, withdrawal, stop and replacement without preparation or activation (test).

OpenShellGitRuntime.repositoryWorkerRestartDisposition reports supported held/preview execution original-worker-required; unsupported legacy routes unknown. Preflight cannot authorize execution.

OpenShellGitRuntime.verifyExecution reports RepositoryExecutionRequiresRedeployError when persisted v5 owner, binding and stable fence prove non-adoptable replacement. ControllerWorker.completeStandingRepositoryWithdrawal rechecks ownership, closes sessions and queues withdrawal atomically. Work records REPOSITORY_EXECUTION_REDEPLOY_REQUIRED; getDeploymentStatus projects repositoryAccess, preserving history (regression).

apps/controller/src/worker.ts:ControllerWorker.processRepositoryCleanup consumes owner-bound Work without admission or delivery. Terminal Work closes attempts and calls stopRevision, preserving replacements. Compute mismatch or stop failure retries. Completion requires disposal and retirement; missing sessions prove neither. Attempts survive deletion.

ControllerWorker.withdrawExpiredRepositoryAccess retires the original version for IAM refusal, confirmed actor or Agent-principal absence, required model-source withdrawal, or unsupported Git withdrawal. Model withdrawal precedes Gateway readiness. Failed reads stay pending unless independent facts prove authority loss. Optional withdrawals cannot justify retirement. recoverPendingCredentialWithdrawals recovers exhausted original/companion work, preserving manual-replay holds. See tests.

updateAgent commits finite removal under Namespace/Agent locks through withdrawFinite: irreversible session closure and cleanup with original deployment authority. The guard rejects sessions. Re-add, Stop supersession and stale-lease transfer cannot restore Git. Pending or denied Stop permits Git detachment, not retirement or running maintenance. Cleanup waits for running intent; authority loss, replacement and terminal retirement win.

PostgresWorkQueue.repositoryExpiryState prioritizes retirement. completeActivatedRevision preserves active removal supervision; REPOSITORY_ACCESS_WITHDRAWN is not activation proof. PostgresOpenShellGitRouteStore.adopt blocks removed Git. Its expire and V2 reduceCredentialPolicy require committed finite removal while stopped. Stopped maintenance defers its claim. Git cleanup may observe pending source effects without replay or completing source withdrawal, then awaits detachment. Other withdrawals remain running-only.

ControllerWorker.processAgentDeletion retires Compute and queues version-keyed cleanup without waiting for sessions. PostgresWorkQueue.completeAgentDeletion calls occ.finalize_agent_deletion to atomically detach attempts, delete live rows and audit. Unresolved provisioning defers completion without consuming retries.

Compute waits for owned Pods to stop before removing material, preserving Secrets referenced by Pods/current Deployments. Deletion requires ownership and UID preconditions; Routes/Deployments also require resourceVersion. Stop preserves newer resources. Shared cleanup retries partial failure; service restart cannot prove remote revocation.

Debugging and Verification

occ agent get AGENT_ID --output json reports activeRevisionId. Inspect REPOSITORY_BINDING_CHANGED, REPOSITORY_CREDENTIAL_DEADLINE_EXCEEDED, REPOSITORY_SESSION_RECOVERY_UNSAFE, REPOSITORY_GIT_TOKEN_QUARANTINED, REPOSITORY_GIT_TOKEN_PENDING, REPOSITORY_CLEANUP_PENDING or REPOSITORY_CLEANUP_COMPLETE. Check registry identity and deadline before retry. apps/controller/src/worker.ts:processRepositoryCleanup logs worker.repository-cleanup-warning with cause once per worker.

repository-not-admitted identifies unselected targets; name-one-repository-target and name-one-repository-ref require explicit selection. Inspect metadata and Pod generation without printing credentials.

Tests distinguish lifecycle, installed-runtime and live-provider proof. Docker/Helm checks exclude CNI; console fixtures exclude model, Slack and GitHub execution. Verify live PR updates, denial before provider contact and continued chat separately.

Search documentation