Repository credential tests
Run these tests from the repository root with Node.js 24, Git, OpenSSL, Docker, and the prepared workspace dependencies. The fixtures generate fresh RSA and TLS material, start bounded local upstreams, and remove their temporary files, listeners and containers when the tests finish. They do not load ambient GitHub credentials.
Reuse fixtures by ownership
Fixtures under tests/fixtures/repository-credentials/ separate data builders,
cleanup, service assembly and controlled provider behavior. Keep fault ordering,
expected values and observable assertions explicit in each scenario. Share setup
by its production owner; fixtures do not establish production authority.
The fixture ownership checks
run in test:conformance and the CI checks-baseline lane. Run them directly
with Node.js 24; they need no Docker daemon or provider credentials:
node --test tests/conformance/repository-credentials-fixture-ownership.test.mjsThey exercise actual subprocess cancellation and the isolation fixture's network cleanup against controlled command responses. These checks qualify the fixture's ownership rules; service, container isolation and live-provider proof remain in their separate suites.
Check source authority boundaries
Run node scripts/verify-repository-credentials-boundary.mjs after changing the
service. The same check runs through pnpm check:workspace in baseline CI. It
parses composition/repository-credentials/, drivers/repo/credentials/,
drivers/repo/github/ and backends/repository-credentials/ beneath
apps/controller/src/, plus
repository-credentials.ts and repository-credentials.mjs, using the workspace's
pinned Prettier TypeScript parser. Runtime imports and re-exports must stay within the
scanned source or use reviewed external modules and named members. Erased
import type and export type declarations remain available; inline type
specifiers can preserve a runtime module load. Provider, metadata and Agent
upstream HTTPS owners have consumer lists; listener, private-file, signing and
client-command owners have separate I/O allowances. New network packages, raw global network or loader
access, and new process-output owners fail the check.
Maintainers own the source guard allowlists. New privileged members, owners, sender consumers or dependencies require explicit security review: identify the authority, caller, scope and protecting negative test. Never substitute wildcard allowances. The guard regression test adds forbidden capabilities to a disposable copy of the real source tree.
Only credential-service configuration may use the GitHub metadata sender: one bounded HTTPS GET per approved repository with a metadata-only token, checking the returned repository ID. Metadata tests cover filtering, routes, retirement and timeouts.
The native hook dispatcher can inspect Git configuration and executable hooks, read Git's hook input and delegate ordinary hooks. It has no direct credential-file reader or network sender. The detached client includes the pure private client-contract validator; negative checks protect these specific I/O boundaries.
This is an accidental-regression guard for reviewed source. It does not perform whole-program dataflow analysis, prove that allowed owners handle secrets correctly, or sandbox malicious code. It does not replace capability design, runtime isolation, or the controlled and live tests below.
For image-pair verification, use the controller and broker compatibility probe.
Run controlled tests
node --test tests/conformance/repository-credentials-backend-conformance.test.mjsnode --test tests/integration/repository-credentials-git.test.mjsnode --test tests/integration/repository-credentials-gh.test.mjsnode --test tests/integration/repository-credentials-long-session.test.mjsThe client integration files create a disposable node:24-bookworm fixture container,
mount the source read-only and mount /usr/bin/gh read-only after checking its
version is exactly 2.100.0. Override the prepared binary with
REPOSITORY_CREDENTIALS_GH_BINARY and the prepared Node image with
REPOSITORY_CREDENTIALS_NODE_IMAGE. These tests require the prerequisites and
fail if they are unavailable. They never install dependencies.
The container resolves credentials.example.test to its own loopback address.
Its network is disabled; every controlled service shares that loopback namespace.
The generated certificate includes that DNS SAN; clients verify it using the
generated public CA. The gateway listens on HTTPS port 443 and gh retains
GH_HOST=github.com. The generated production client configuration and launcher
own authentication, clean environment setup and the Git helper. No insecure
TLS switch or localhost GH_HOST substitute is used.
Characterize service loss
node --test tests/integration/repository-credentials-service-loss.test.mjsThese cases run the service and GitHub factory in a child process against a controlled provider. They cover stale-bearer denial, uncertain issuance, a lost revocation response and monotonic cleanup time. Bound admission fails closed without the durable journal. These cases do not prove PostgreSQL recovery.
The fixture joins each child death and removes only its verified stale socket before replacement. This setup does not prove automatic stale-socket recovery. These cases use synthetic keys and controlled time, with no live GitHub, database, Kubernetes or model execution. Safety failures and cleanup failures fail normally.
Verify Agent admission and durable ownership
See platform qualification for prerequisites, commands and proof limits.
Exercise the controlled platform path
See platform qualification for prerequisites, commands and proof limits.
What the controlled service tests prove
The Git upstream runs the actual git-http-backend against a disposable bare
repository. Contributor (git-write) coverage exercises clone, fetch, branch
checkout, push and PR work. Reader (git-read) coverage admits reads, rejects
push and REST writes before token acquisition or upstream access, and checks
that a denied push leaves remote refs unchanged. Collaborator (git-full)
adds ordinary issue management.
A fault case drops the response after receive-pack finishes and checks that the
service sends the push once while the remote ref records the accepted commit.
The API upstream verifies RSA signatures, App identity, current JWT time, repository selection and complete permission maps. It maintains independent PR, issue and comment state. The pinned CLI runs REST creation/read/update, paginated comments and issues using native repository-ID links and opaque issue cursors, individual comment operations and native GraphQL PR creation. It checks bodyless deletion and unchanged human text. Unknown routes and lost mutation responses exercise denial and no-replay behavior.
The access-level cases send requests through the real TLS listener, acquisition and forwarding path. They cover exact permission maps, rejection of an older grant through the private control API, bounded README/diff replies and possible-write/no-replay accounting for Reader GraphQL. The controlled upstream models GitHub responses; it does not establish live GitHub authorization or installed-Agent qualification.
The alternate adapter uses a nonnumeric repository ID, nested repository path, different native authentication and permissions, short access expiry, and a separately bounded private renewal secret. The production service supplies custody, original attempts, leases, settlement, expiry and finalization. The HTTP case uses the same production listener and sender as GitHub. Passing this test does not claim support for another production provider.
The long-session test clones once, advances trusted wall and monotonic clocks past hour thirteen, then pushes and performs the API workflows through the same running service and unchanged client files. The upstream checks that expired token A receives no later authentication attempts, including rejected attempts at the Git and API boundaries, and token B has a fresh JWT with the same repository and profile. It then checks local closure and provider retirement separately. There is no long sleep and no Agent-facing clock control.
The control integration suite loses an admission response through an actual Unix socket relay, then reconciles public status with the original admission ID. It also destroys the listener socket during construction to distinguish known nondelivery from ambiguous response loss. Owner regressions cover delayed settlement, frozen or adjusted wall clocks, short configured safety margins, and cleanup retaining credential material after authentication becomes ineligible.
Check native Git selection
The client configuration and router cases use stock Git with generated native
configuration. They cover endpoint case and optional .git spelling, exact
host/port/username, staged preparation, private files and CA agreement. Duplicate
bindings require explicit valid pins; stale pins, expired selections and
generation replacement release no bearer. Local hooks, aliases, moves, removals
and worktrees remain native Git behavior. Run both files directly:
node --test tests/integration/repository-credentials-client-config.test.mjs \ tests/integration/repository-credentials-router.test.mjsThe real Git journey verifies committed moves/removals and upstream refs. The gh case records native child Git through private HOME configuration. The installed image's system include, multi-repository registry and Compute publication require separate platform qualification; helper selection alone does not prove them.
Run the gateway normalization cases with the same prepared image and pinned gh:
node --test tests/integration/repository-credentials-native-paths.test.mjsFive container cases exercise stock Git through canonical GitHub URLs with mixed
owner/repository case, with and without .git. They verify fetched objects,
accepted refs/content and canonical upstream paths through the real classifier,
HTTPS sender and git-http-backend. Cold discovery challenges issue no token;
read-only receive-pack and raw-path/API denials contact no upstream. These cases
do not establish literal-.git repository or multi-repository registry runtime
support. Count the child cases separately from the host wrapper.
The repository-credentials-container CI lane selects this file through the
suite map, alongside the other controlled
client tests. Selected prerequisites, failures, skips and cleanup outcomes remain
part of CI result accounting.
Qualify emitted artifacts
Build the final artifacts and run the detached package check first:
pnpm credentials:buildnode --test tests/integration/repository-credentials-package.test.mjsThe builder starts from the controller's emitted code and writes separate
.build/repository-credentials/service and .build/repository-credentials/client
closures. The service includes repository-credentials.js and
composition/repository-credentials/check-config.js; the client includes
drivers/repo/github/credentials/client/{launch,operator,git-helper,native-git,router}.js and their
runtime dependencies. Detached loading must work without workspace source or
runtime node_modules.
The detached check starts the emitted service, admits and closes a session over
its Unix socket, and invokes the emitted launcher and Git helper. It also prepares
native Git configuration from staged session material, moves it to its final
path, and checks the manifest helper and router error boundary after deleting the
build workspace. This proves detached loading; installed system configuration and
ordinary-Agent routing require the platform checks. The context case rebuilds
both Docker inputs and rejects source files, compiler artifacts and linked inputs.
Build the service and client images using the operator guide. Then combine those artifacts in an owned test-only image. Use the same local Docker builder for all three builds so it resolves the delivered input images:
docker build --builder default --load --pull=false \ --build-arg SERVICE_IMAGE=repository-credentials:local \ --build-arg CLIENT_IMAGE=repository-credentials-client:local \ -f tests/fixtures/repository-credentials/Dockerfile.qualification \ -t repository-credentials-qualification:test .REPOSITORY_CREDENTIALS_TEST_IMAGE=repository-credentials-qualification:test \ node --test tests/integration/repository-credentials-container.test.mjsRecord the source commit/tree, working-tree changes, both input image IDs and the qualification image ID with results:
docker image inspect --format '{{.Id}} {{json .Config.Entrypoint}}' \ repository-credentials:local repository-credentials-client:local \ repository-credentials-qualification:testThe first case imports /app/dist, exercises the emitted production service and
client entrypoints, and uses the same long-session acceptance sequence. The
second runs alternate-backend conformance through those emitted common owners,
including renewal, private authentication, and streamed callback drainage. The
qualification image is a test driver; it is not a separate supported deployment.
Without an explicit image selector, these cases report a skip. Source test
success alone does not establish this artifact result.
The service, upstream fixtures and clients run together inside that test driver.
Passing it proves emitted-artifact composition and forwarding. The Compose case
renders deploy/examples/repository-credentials/compose.yaml and checks declared
mount separation; it does not start those services.
Check the runtime image's private material volume
Run the separate runtime volume test against an image built from the candidate source:
OCC_TEST_RUNTIME_IMAGE=openclaw-enterprise-runtime:test \ node --test tests/integration/repository-runtime-volume.test.mjsThe required images-packaging lane runs this case with the image-installed
client and no detached bundle overlay. It checks both initializers, a root-owned
fsGroup-style tmpfs parent, private subPath mounts, ownership rejection, retry
and read-only delivery. An unset selector skips standalone execution; the CI
lane rejects skips. See the linked image guide for Docker prerequisites and
proof limits.
Verify separate running containers
Select both delivered images to run the distinct isolation case:
REPOSITORY_CREDENTIALS_SERVICE_IMAGE=repository-credentials:local \REPOSITORY_CREDENTIALS_CLIENT_IMAGE=repository-credentials-client:local \ node --test tests/integration/repository-credentials-isolation.test.mjsThe harness resolves the selectors to different immutable image IDs, starts
separate service and client containers, and inspects running mounts, processes,
client files and sanitized outputs. The client receives the selected session,
public trust and workspace; service inputs, the private control socket and
sibling sessions remain outside its mounts. Actual Git and pinned gh use the
service against a controlled provider. This proves the tested ordinary-container
custody boundary, without a network-confinement or live-GitHub claim.
Omitting both selectors skips this case; selecting only one fails. Selected images, Docker and other required prerequisites must be available. Record the exact images, case results and cleanup outcome separately from the combined qualification image and rendered Compose check.
Record each evidence boundary
| Check | Evidence it can establish |
|---|---|
| Source tests and source guard | Behavior of real owners against named protocol fixtures; reviewed import/I/O boundaries. |
| Detached package check | Emitted entrypoints and runtime dependency closure without source fallback. |
| Runtime private volume | Image-installed client with real Docker tmpfs and private subPath mounts; ownership, retry and read-only delivery. |
| Combined qualification image | Emitted service/client composition, controlled hour-13 push/API operations and alternate-backend conformance. |
| Rendered Compose | Declared paths and mount separation. |
| Separate running containers | Delivered image identity and observed client/service custody for the exercised commands. |
| Authorized live smoke | Real provider behavior and cleanup for the selected repository, grant and client version. |
| Installed Agent turn | The configured Agent executes Git through its own Harness, tool policy, network path and repository authorization. |
Retain selectors, versions, source/artifact/image identities, pass/fail/skip counts and cleanup results. Missing selectors leave evidence unavailable; they do not qualify the corresponding boundary. Historical installed or live results stay bound to their original artifacts. After changes, record justified equivalence for each affected assertion or rerun its owning check. These packaging checks do not establish OCC/worker/Compute integration, an installed ordinary-Agent model contribution, a real-time thirteen-hour soak or release readiness.
For Codex consumers, do not substitute operator kubectl exec, direct container
Git commands, or runtime-image smoke tests for the installed Agent turn. Those
checks can prove material delivery, Git configuration and broker authorization
outside Codex. Exercise stock Codex's generated broker allowance,
allow_local_binding = true, and mode = "full" separately. Cover bound/unbound
dedicated/embedded consumers, broker-host denies, and unallowed hosts. Otherwise allowed private
addresses are permitted. Require real reads, an authorized temporary write, and
an unauthorized operation denied by broker authorization.
For Slack-enabled Agents, start the installed-Agent proof from Slack and verify
the threaded Agent response instead of using a direct native UI prompt.
Run an authorized live smoke
Prepare a running gateway on valid DNS/TLS port 443, its private operator socket, and a disposable repository explicitly authorized for temporary branch, PR, issue and comment writes. Use the exact pinned client. Select only this file:
REPOSITORY_CREDENTIALS_LIVE=1 \REPOSITORY_CREDENTIALS_LIVE_AUTHORIZED=1 \REPOSITORY_CREDENTIALS_LIVE_CONTROL_SOCKET=/run/credential-service/control.sock \REPOSITORY_CREDENTIALS_LIVE_CA=/run/credential-service/public-ca.pem \ node --test tests/integration/repository-credentials-live.test.mjsThe smoke admits a five-minute git-full session through the real control API.
It clones and pushes unique temporary branches, exercises REST and native PR
creation plus issue/comments. Before each create, it registers reconciliation
using a unique run marker and, for PRs, the unique head branch. Cleanup inspects
at most five pages of 100 resources through existing routes in the admitted
repository; it closes or deletes only a single matching owned resource. A lost
creation response never causes creation to be replayed. Missing, ambiguous or
truncated identity inspection fails cleanup and reports the run marker for
operator reconciliation.
Work commands have a 90-second overall budget and terminate their owned process
groups on timeout, output overflow or cancellation. Resource cleanup has a
separate 60-second budget. Session cleanup always runs afterward, validates the
close acknowledgement, and polls status for disposal under a 10-second polling
budget; each control request also has its production five-second deadline.
Local CLOSED status is distinct from DISPOSED: active uses, pending or
uncertain credentials, and auxiliary obligations must all resolve. Cleanup
failures fail the test and require operator review of the disposable repository. Keep provider keys and installation tokens on the
service side; the test client receives only the gateway bearer.
Without the live selector the case explicitly reports unavailable live-provider evidence. If selected, missing authorization, socket, CA, routing or credentials fails. Controlled upstream success proves service behavior against those protocol fixtures; it does not establish live GitHub compatibility or a real thirteen-hour provider soak.
Qualify an installed Agent against GitHub
See platform qualification for prerequisites, commands and proof limits.
