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
apps/controller/src/repository-credentials.ts:maindelegates protected-path startup tostartCredentialServicein controller composition.apps/controller/src/drivers/repo/github/access/client/operator.ts:callControlcarries operator admission/status/close requests over the private Unix socket.apps/controller/src/drivers/repo/access/server.ts:startListenersaccepts HTTPS client traffic after protected startup succeeds.
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:
- Prepared: Pass the original authority and selection to
createPreparedand verify the returned binding. The sender reads READY material from the worker Token Service; it cannot mint installation tokens. It loads neither development custody nor the development lifecycle. - Development: Construct custody and lifecycle locally
and drain failed construction. The GitHub factory
uses the grant resolver and
client authentication.
It requires both
developmentOnly: trueand--development-authority.
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.
