OpenClaw EnterpriseDOCSGitHub

Repository credential service flow

Overview

Operators or workers admit sessions over a private Unix control socket. Sessions have a maximum duration and a capped count. Clients send Git or selected API requests over HTTPS with a gateway bearer. This trace covers forwarding, closure and cleanup in the separate credential process. The Agent flow owns admission and material delivery.

Entry Points

Standalone configuration selects one repository; the registry supports several under one App installation. Sessions bind exactly one repository. Operators own protected files and control; clients receive session files and public connection/trust settings.

Flow

graph TD
  Operator["Worker or operator<br/>opens session"] --> Admission["Reserve slot, resolve grant<br/>then construct session"]
  Admission -->|Construction failed| ConstructionCleanup["Seal renewal access<br/>drain unpublished custody"]
  Operator -->|Uncertain admission| Recovery["Inspect original owner<br/>and known session status"]
  Admission --> Files["CLI publishes private<br/>client directory"]
  Files --> Client["Stock Git helper or pinned gh<br/>selects exact session"]
  Staged["Staged runtime generation"] --> Native["Prepare native include<br/>without reading bearers"]
  Native -->|Compute publishes complete generation| Client
  Client --> Route["Validate auth, profile route<br/>and request capacity"]
  Route -->|Denied| Denial["Local failure without private details"]
  Route -->|Admitted| Credential["Reuse valid credential<br/>or acquire and capture"]
  Credential -->|Uncertain issue| Blocked["Block minting<br/>retain cleanup obligation"]
  Credential -->|Usable| Gate["Authorize first write<br/>recheck lease and deadline"]
  Gate --> Upstream["Upstream exchange with size limits and deadlines"]
  Upstream --> Result["Response or uncertainty<br/>without write replay"]
  Operator -->|Close| Closed["Deny local use<br/>cancel exchanges"]
  Closed --> Cleanup["Join original settlement<br/>retire and finalize"]
  Cleanup -->|Resolved| Disposed["Session disposed"]
  Cleanup -->|Unresolved| Pending["Pending records within session capacity"]

Execution Trace

1. Load protected startup inputs

Both checkConfiguration and startCredentialService use readProtectedFile and validate gateway, session and capacity settings. The configuration flow covers protected paths and Kubernetes projections. The standalone check needs no listeners or sessions.

apps/controller/src/composition/repository-credentials/service.ts:runService uses the configured factory's createPrepared capability to select a session constructor before either listener starts. Both constructors share admission, capacity and shutdown:

The public contracts define RepositoryPolicyDriver, NativeRepositorySender, grant identity and four-field status. Private contracts separate admission and bearer results, the six-field client DTO and backend custody handles. Type-only imports keep service startup out of Compute and Git/gh. The frozen facade exposes open, status, close and shutdown; listeners own exchanges. See the Agent flow for platform session control and policy.

The prepared factory uses the API and worker's canonical registry and preserves repository identity and access profile. The worker owns the App signing key; the sender holds its TLS private key. Startup reclaims only a refused, private, owned Unix socket whose identity is unchanged. Neither listener admits requests until the control socket has mode 0600 and its identity is recorded.

2. Admit and publish a client session

apps/controller/src/drivers/repo/access/control.ts:handleControl validates the local request method, target, content type, and body before invoking the common service. The body limit is the smaller of 16,384 bytes and controlBodyBytes. apps/controller/src/drivers/repo/access/service.ts:createCredentialService reserves capacity through binding resolution and construction. Shutdown cancels unclaimed selections and drains in-flight work within grace; expiry or shutdown closes unpublished sessions. GitHub maps git-read, git-write and git-full to permissions; startup rejects unknown profiles. Only the original response contains the bearer; lookup stores its digest.

apps/controller/src/drivers/repo/access/server.ts:startListeners owns the correlation registry from apps/controller/src/drivers/repo/access/control.ts:createControlAdmission. The registry holds at most twice the configured sessions count. Admission IDs bind the complete request: platform Namespace, repository reference, normalized profile, expected grant and absolute deadline. Registry mode requires this binding and independently resolves its fingerprint. The broker holds one local session slot and freezes the selected route before the worker-owned private journal reserves the original attempt. It constructs that route only after the reservation succeeds, using the original deadline. A capacity or shutdown refusal before reservation leaves no receipt; a missing or uncertain reservation creates no session. Recovery durably fences a missing admission. If the broker cannot open or bind after reservation, it fences its own reservation. An uncommitted fence stays with its admission for the next open or recovery; past the admission window a sweep retries it and forgets it once committed, so it stops counting toward capacity. Known nondelivery closes the session. Reconciliation cannot recover a bearer, change its binding or replay provider work. Keep the original admission ID and inspect its owner and any known session after an uncertain response. Status observes expiry without closing the session or starting disposal; clock rollback cannot restore use. If a disposal acknowledgement is lost, status, close and shutdown resend the same status; the journal records it once and refuses a different one.

Control and receipt use readPrivateJsonResponse: complete application/json, 16,384-byte cap, buffer wiping. Synchronous overflow refusal precedes teardown: receipt reports RECEIPT_UNAVAILABLE; backend control reports retryable RepositoryCredentialControlError. callControl retains control-request-failed, the admission ID, and request teardown.

Development factory failure or an invalid binding closes construction admission before apps/controller/src/drivers/repo/access/custody.ts:disposeAllRenewal. All handles are sealed before awaiting callbacks. Unpublished custody occupies the configured session capacity until callbacks settle and bytes are disposed.

apps/controller/src/drivers/repo/github/access/client/config.ts:writeClientConfiguration sets files 0600 and directories 0700 despite umask. operator open checks --output before admission and again when writing; after a write failure it closes the session and reports its ID.

3. Authenticate the selected client

apps/controller/src/drivers/repo/github/access/client/native-git.ts:prepareNativeGitConfiguration prepares staged material through apps/controller/src/drivers/repo/github/access/client/manifest.ts:readRuntimeRepositoryManifest which validates complete-generation public identities, final paths and metadata. The renderer requires same-origin CA agreement and one gateway origin per canonical host, then emits deterministic rewrites and origin-scoped credential/TLS defaults. Exclusive no-follow creation writes staged gitconfig; the receiver rechecks deadlines and publishes atomically. Source does not qualify installed images or receivers.

Stock Git reads the resulting include with its normal semantics. On authentication, apps/controller/src/drivers/repo/github/access/client/git-helper.ts parses credential protocol input of at most 16 KiB and verifies the embedded generation. apps/controller/src/drivers/repo/github/access/client/targets.ts:selectGitCredential matches effective HTTPS origin and case-insensitive owner/repository with optional .git. Explicit reference/session pins must match. Ambiguity, stale pins and expired selections release no bearer. The helper reads only the selected bearer, clears its buffer after use, and leaves material unchanged for store/erase.

apps/controller/src/drivers/repo/github/access/client/router.ts:routeRepositoryClient handles only gh. It validates the supported API/explicit-head PR invocation, selects a binding from its explicit target, inherited pin or native Git remotes, and propagates the exact generation/reference/session pin to children. Before launching gh, the router and single-session launcher compare the private bearer and generated host configuration and check the deadline again after reading them. A mismatch or unsafe file refuses the launch. This preflight is not an atomic snapshot or authorization. apps/controller/src/drivers/repo/github/access/client/environment.ts:createClientEnvironment keeps gh token/config isolation while preserving normal HOME and system Git configuration. apps/controller/src/drivers/repo/github/access/client/commands.ts:prepareGhCommand checks gh 2.103.2 and the canonical host/port profile. apps/controller/src/drivers/repo/github/access/client/launch.ts:launchClient provides single-session stock Git execution, signal forwarding and original child exit status. executeClientCommand preserves numeric exits and maps signal termination to 128 + signal number (SIGINT 130; SIGTERM 143), without Git command parsing or temporary-HOME cleanup. The reference owns configuration overrides and generation limits.

4. Reserve, acquire and dispatch

apps/controller/src/drivers/repo/access/server.ts:startListeners bounds sockets per listener, preserving control admission under public saturation. HTTPS separately bounds handshakes and starts headerMs on the TLSSocket at secureConnection; Unix control retains its raw-socket timer. apps/controller/src/drivers/repo/access/transport/agent.ts:createAgentHandler clears the TLS timer after authenticated capacity reservation, before acquisition; the exchange deadline remains enforced.

createAgentHandler checks framing and delegates authentication to the bound factory. Unauthenticated valid Git routes receive a Basic challenge; authenticated requests reserve capacity before forwarding bodies. Each refusal of an authenticated request, and each framing refusal whose credential names an open session, emits one repository-credentials.denied record. GitHub plans routes through apps/controller/src/drivers/repo/github/access/routes.ts:createRoutePolicy, using apps/controller/src/drivers/repo/github/access/routes/classification.ts:classifyRoute to classify target, method, profile and query. All profiles admit upload-pack discovery and execution; git-read rejects receive-pack, including discovery. Git paths match admitted owner/repository case-insensitively with one optional .git suffix, then use the canonical identity upstream. A literal .git repository keeps its suffix. Raw-target, endpoint, method, media, query and profile checks remain. All profiles admit selected repository, README, PR, issue and comment reads, GET /meta and POST /graphql. REST writes require the matching profile permission; Reader rejects them before dispatch. The shared PR/issue comment routes also rely on GitHub's resource authorization. Raw README and diff/patch replies retain the configured API response-size limit and bypass JSON rewriting. GraphQL uses the exact installation-token grant without field-level or branch-only authorization. GitHub may return permitted public information; every GraphQL POST is a possible write. The GraphQL plan carries apps/controller/src/drivers/repo/github/access/graphql-input.ts:allowsGraphqlInput: apps/controller/src/drivers/repo/access/transport/agent.ts:createAgentHandler buffers the body within the input bounds before credential use and refuses invalid JSON or any decoded string naming tempCloneToken with 400.

apps/controller/src/drivers/repo/access/service.ts:createCredentialService reserves the exchange. apps/controller/src/drivers/repo/access/lifecycle/exchange.ts:executeExchange reuses a valid credential or acquires one through apps/controller/src/drivers/repo/access/lifecycle.ts:createLifecycle. Concurrent misses share acquisition; the last departing waiter cancels it. Until settlement, dependent requests return not-dispatched without new acquisition. The original owner retains capture, settlement and cleanup obligations. Use lifetime starts at capture and cannot exceed cleanup expiry; wall-clock changes cannot shorten custody or restart use lifetime. An exchange retains its selected credential through replacement. Its selected authenticator gets a temporary copy, cleared after use; a reference alone cannot retrieve authentication bytes. Production native requests use apps/controller/src/drivers/repo/github/access/warm-use.ts:authorizePreparedGitSend. The worker validates the live grant and lends prepared material for that exchange. executeExchange checks the session before and after prepared authorization. apps/controller/src/drivers/repo/access/transport/upstream.ts:createUpstreamSender checks it again at the first HTTP write. If an issued permit cannot be submitted, the original settlement receives not-submitted. Missing or uncertain material does not cause request-time issuance. The explicit development token factory instead borrows its process-owned static token.

Handler and sender each capture allowed origins. apps/controller/src/drivers/repo/access/transport/request-headers.ts:createUpstreamHeaders enforces adapter header byte and count limits, rejects duplicates case-insensitively, and strips reserved fields such as Expect before request construction. After TLS, the first-write gate rechecks the owned lease or the original prepared permit, cancellation and deadline. Withdrawal blocks new permits; an issued permit allows one send before its deadline. Denial or confirmed pre-write closure sends no HTTP request. The response-header timer starts after upload unless headers arrived. Connection, upload, stall and total deadlines apply. Upload-pack uses separate response-byte, first-header, stall and total budgets. Plans obey maxExchangeMs and session deadlines; static-token upload-pack also obeys its lease. Byte counting applies backpressure without pack buffering.

5. Deliver an outcome and release ownership

apps/controller/src/drivers/repo/access/transport/response-headers.ts:responseHeaders checks upstream header bounds and framing. GitHub uses apps/controller/src/drivers/repo/github/access/response.ts:createResponsePolicy for headers, pagination and followed resource fields, composing apps/controller/src/drivers/repo/github/access/response-urls.ts:createUrlRewriter and rewritePaginationLinks with apps/controller/src/drivers/repo/github/access/response-resources.ts:createResourceRewriter. Before releasing size-limited JSON, it removes temp_clone_token from repository objects, their parent/source relationships and PR head.repo/base.repo. GraphQL cannot select tempCloneToken. This is not a generic credential-string scanner. The URL owner maps the configured repository ID to admitted /repos/owner/repository before checking route and resource purpose; it refuses other IDs. Issue pagination accepts after/before cursors with 1–1,024 allowed characters and up to two trailing = characters. Rewritten gateway links require subsequent session/profile checks. Nested labels, milestones, repository metadata and human content remain unchanged. Transport applies apps/controller/src/drivers/repo/access/transport/response-headers.ts:safeResponseHeaders before writing client headers.

After successful input, failure before upstream headers stops upstream I/O, then sends fixed 502 exchange-uncertain after settlement. Cancellation, deadlines, input failure and post-header failure destroy the incoming connection, including cancellation during error delivery.

apps/controller/src/drivers/repo/access/lifecycle/exchange.ts:executeExchange closes a rejecting authenticator's gate, drains its admitted sender, then classifies the result: completed, not-dispatched or possibly-dispatched. Committed writes remain possibly dispatched, never replayed. It joins tracked I/O before releasing credential use and capacity. The handler ignores response close after writableFinished; premature close cancels the exchange. Original provider settlement and cleanup outlive requests.

6. Close locally and finish cleanup

apps/controller/src/drivers/repo/access/service.ts:createCredentialService closes sessions before exchange cancellation and cleanup. Disposal awaits exchanges, original actions, captures, renewal state and auxiliary finalization. Before dispatch, a full provider queue makes apps/controller/src/drivers/repo/access/lifecycle.ts defer retirement and finalization, registering one waiter per session via apps/controller/src/drivers/repo/access/provider-queue.ts. Available capacity wakes cleanup. Queue rejection alone never marks an action uncertain.

apps/controller/src/composition/repository-credentials/service.ts:runService stops admission, closes sessions and independently bounds cleanup. Grace expiry reports unresolved obligations. The original broker journals DISPOSED in the worker before reporting it. Graceful exit allows ten extra seconds for terminal writes. The Helm worker’s restartable init container preserves receipts through broker shutdown. Both private listeners (receipt and prepared Git) disconnect clients and join admitted handlers before Worker pool teardown.

For claimed closing protocol 1/3 cleanup, apps/controller/src/worker/repository-credentials.ts:consumeDisposedReceipt reads the original broker's receipt. A DISPOSED hint starts a transaction: check the claim, lock and match the attempt, validate that receipt, recheck the claim, record disposal. Committed receipts supply missing session IDs. If locked reads cannot confirm it, cleanup stays pending without sender calls. Failed initial lookups or those without terminal receipts use the attempt's original sender.

Only committed observations survive restart. Uncertain provider inventory stays unknown. The original issuer retains token cleanup. The sender closes TLS and session material after shutdown disposition. Failed construction remains unpublished, undisposed and counted in pendingAuxiliary. Disposal wakes shutdown waiters.

Emitted packaging

scripts/build-repository-credentials.ts packages the controller's emitted credential entrypoints into separate service/client artifacts.

Artifact context Runtime entrypoints beneath dist/
.build/repository-credentials/service repository-credentials.js, composition/repository-credentials/check-config.js
.build/repository-credentials/client drivers/repo/github/access/client/{launch,operator,git-helper,native-git,router}.js

deploy/runtime/repository-credentials/ Dockerfiles consume these contexts. deploy/examples/repository-credentials/compose.yaml separates service inputs/control from client session/workspace mounts. See image builds and entrypoints. The test guide distinguishes packaging, container, live-provider and platform proof.

Debugging and Verification

Check emitted startup with pnpm credentials:build and pnpm credentials:check-config CONFIG_FILE. Client configuration and package integration tests cover private files, Git helper behavior and detached loading. After close, inspect session status for pending or uncertain cleanup.

Helper failures report fixed categories, never credentials. Check HTTPS host/path and private file ownership. For API failures, check git-full, the pinned CLI, canonical host, gateway DNS/SAN and port 443. Inspect gh session files and ownership without exposing credentials. The test guide covers controlled upstream, client and alternate-adapter checks; qualify live providers separately.

Search documentation