Clone stage per tech spec §6, verbatim mechanics: every git invocation carries a BLANK credential.helper first (suppresses OS keychain helpers) then the inline veect helper; the PAT reaches git via child env only — never argv, URLs, logs, or .git/config. GIT_TERMINAL_PROMPT and GIT_ASKPASS are pinned so a headless clone can never hang on a TTY or ambient IDE askpass. Staged status events (start/heartbeat/final) + ready on success; named failures: invalid-git-url, auth-failed, clone-failed (daemon's 'repository not exported' maps to bad-URL, not auth). Integration suite clones the daemon-served vite-kitchen and an authenticated dumb-HTTP remote that asserts the exact Basic credential git presented, plus leak assertions over logs/argv/.git. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
4.1 KiB
apps/runner — workspace runner (cloud-runner process)
The runner process of ADR-027: executes a workspace's repo code (clone,
install, scan, preview adapter) on behalf of the control plane, speaking
@repo/core-runner-protocol over plain WS/JSON. App-tier (tags: ["app"]): imperative code is expected here; there is no feature
manifest, no use-case layer, no DI container.
Process contract (what the provisioner — story 06 — relies on)
- Config is env-only (see
src/config.ts):RUNNER_TOKEN(required, secret — never argv),RUNNER_WORKSPACE_DIR(required),RUNNER_PORT(default0= ephemeral),RUNNER_HOST(default127.0.0.1),RUNNER_HEARTBEAT_MS(default1000). - Port discovery: on boot the runner writes a JSON line to stdout:
{"level":"info","event":"listening","host":...,"port":...}. - Logs are JSON lines on stdout. Log event names + safe fields only — never protocol payloads, never credentials. Integration tests grep the entire child output for the PAT and the workspace token.
SIGTERM/SIGINTshut down gracefully.
Protocol session semantics (documented in src/server.ts)
- Every inbound/outbound frame is envelope-wrapped and zod-parsed
(
envelopeSchema); rejections emit named errors (unsupported-protocol-version/invalid-message/unauthorized). Pre-handshake rejections and token mismatches close the socket (1008). - First message must be
hello; the runner answersready. - Commands run one at a time in arrival order. A running stage streams
status(stage + elapsedMs: start, heartbeats, final). Success ends withready(the idle marker the story-07 orchestrator drives on); failure ends with a namederrorand the session stays open. - Zod issues are summarized as path + code only — payload values (which may include a PAT) are never echoed into errors or logs.
Clone stage — credential mechanics (tech spec §6, verbatim)
- Every git invocation carries
-c credential.helper=(BLANK first — resets the helper list, suppressing OS keychain helpers) followed by-c credential.helper=<veect-helper>(an inline shell function that answersgetfrom env vars and ignoresstore/erase). - The PAT reaches git via the child's env (
VEECT_GIT_PAT) — never argv (the helper string only names env vars), never in the URL, never written to.git/configor anywhere else on disk. GIT_TERMINAL_PROMPT=0andGIT_ASKPASS=echoride every invocation: a clone must never hang on a TTY prompt or an ambient IDE askpass.- Failures map to named causes (
src/stages/clone.ts): unusable URL / unreachable repo →invalid-git-url, credential rejection →auth-failed, anything else →clone-failed.
How the auth path is honestly tested
git daemon (story 01's transport) has no authentication, so it cannot
prove credential delivery. The integration suite therefore also serves
the same bare fixture over authenticated dumb HTTP
(tests/http-git-server.ts): a real git clone probes, receives 401,
consults the ephemeral helper, and retries with Basic auth — the test
asserts the server received exactly x-access-token:<PAT>, that git's
first probe was unauthenticated, and that the PAT appears nowhere in
runner logs (which include every spawned git argv), .git/config, or
any other file under .git/. Wrong/missing PAT → named auth-failed.
What is NOT covered: a real smart-HTTP provider (GitHub et al.) — the
dumb-HTTP fallback exercises the same credential machinery in git, but
the smart-protocol surface itself first meets reality in later PRDs.
Testing
- Unit suites live next to sources in
src/; protocol/WS suites live intests/(protocol-server.test.tsruns the real server in-process so v8 coverage sees it;runner-process.integration.test.tsspawns the real child process and discovers the port from stdout). tests/protocol-client.tsenvelope-parses every inbound frame, so every test doubles as an outbound-conformance assertion.- Coverage: app-tier vitest thresholds inherited from
vitest.base.node; onlysrc/main.ts(bootstrap glue, exercised by the spawn suite in a child process) is excluded.