Context
ORB already has a proven pattern for Docker/Swarm/K8s-secret-mount indirection in src/selfhost/load-file-secrets.ts, which lets an operator supply a credential via a file path instead of a plaintext env var. gittensory-miner has no equivalent: DEPLOYMENT.md's fleet-mode example only shows -e GITHUB_TOKEN as a plain env var, which means the token (and any coding-agent credential) is visible in plaintext via docker inspect on any host running the miner. This issue ports that pattern into the miner package.
Dependencies
None — independently shippable. It does not require docker-compose.miner.yml (a separate sibling issue in this batch) to exist first — the _FILE indirection is a config-resolution change usable with a plain docker run today. It does, however, add new env-var names, so if the generated env-var reference issue in this same batch (npm run miner:env-reference) lands first, this PR should run that generator as part of its own commit to keep the reference in sync; if this PR lands first, note in its own follow-up that the reference generator will need to pick these up.
Requirements
- In the module(s) under
packages/gittensory-miner/lib/ that currently resolve GITHUB_TOKEN and the coding-agent credential env vars (per MINER_CODING_AGENT_PROVIDER: an API-key env var, or an auth-file path for claude-cli/codex-cli), mirror src/selfhost/load-file-secrets.ts's pattern.
- For each supported credential, check for a companion
<NAME>_FILE variable (e.g. GITHUB_TOKEN_FILE) in addition to the plain <NAME> variable.
- If
<NAME>_FILE is set and the plain <NAME> is unset, read the file's contents (trimmed of trailing whitespace/newlines) and use that as the effective credential value.
- If both
<NAME> and <NAME>_FILE are set, follow the same precedence rule as load-file-secrets.ts (state explicitly, in code comments and docs, which one wins) rather than silently picking one.
- If
<NAME>_FILE is set but the file is missing or unreadable, fail with a clear, actionable error identifying the file path — never silently fall through to an empty/undefined credential.
- Apply this to
GITHUB_TOKEN and to whichever credential(s) the currently-configured MINER_CODING_AGENT_PROVIDER requires.
- Never log the resolved secret value itself in any code path this issue touches — only whether a credential was sourced from an env var or a file.
- Update DEPLOYMENT.md's fleet-mode example to show the
_FILE alternative (e.g. mounting a Docker/Swarm/K8s secret at /run/secrets/<name>) alongside the existing plain-env-var example.
- Do not touch any autonomous-loop, governor, or attempt/claim control-flow — this is credential-resolution plumbing only.
Deliverables / Acceptance Criteria
Test Coverage Requirements
This PR must ship with full test coverage for every changed line and branch — the repo's Codecov patch gate requires 99%+ coverage and the house standard is to aim for 100%, including both sides of every conditional/nullish-coalescing branch introduced. Add: (1) unit tests covering the new/changed logic's success and failure paths (plain var only, _FILE only, both set, neither set, file set but unreadable/missing, file set but empty), (2) an invariant test asserting the resolver never logs or returns the raw secret value anywhere in its output/log calls — only a boolean/source indicator — and (3) not applicable as a bug-fix regression test since this is net-new functionality, but include a test that an operator's existing plain-env-var setup keeps working unchanged (no regression to today's behavior).
Codecov visibility note: packages/gittensory-miner/**, apps/gittensory-miner-ui/**, and apps/gittensory-miner-extension/** currently sit entirely outside vitest's coverage.include glob, so codecov/patch cannot measure changes there yet — closing that gap is exactly what #4864 and #4865 (in this same milestone) do. Any part of this change under packages/gittensory-engine/src/** or the repo's own src/** remains fully Codecov-instrumented as usual. Either way, this does not lower the bar: treat the 100%-including-invariants-and-regression target above as the enforced house standard regardless of what Codecov can currently see, and it becomes gate-enforced for real once #4864/#4865 ship.
Expected Outcome
An operator running AMS in fleet mode (Docker Swarm/Kubernetes) can mount GITHUB_TOKEN and coding-agent credentials as managed secrets instead of passing them as plaintext env vars visible via docker inspect.
Links & Resources
src/selfhost/load-file-secrets.ts — the pattern being ported
packages/gittensory-miner/lib/ — where credential resolution needs the new indirection
- DEPLOYMENT.md — fleet-mode example to update
- Theme: Self-host packaging & docs
Context
ORB already has a proven pattern for Docker/Swarm/K8s-secret-mount indirection in
src/selfhost/load-file-secrets.ts, which lets an operator supply a credential via a file path instead of a plaintext env var.gittensory-minerhas no equivalent: DEPLOYMENT.md's fleet-mode example only shows-e GITHUB_TOKENas a plain env var, which means the token (and any coding-agent credential) is visible in plaintext viadocker inspecton any host running the miner. This issue ports that pattern into the miner package.Dependencies
None — independently shippable. It does not require
docker-compose.miner.yml(a separate sibling issue in this batch) to exist first — the_FILEindirection is a config-resolution change usable with a plaindocker runtoday. It does, however, add new env-var names, so if the generated env-var reference issue in this same batch (npm run miner:env-reference) lands first, this PR should run that generator as part of its own commit to keep the reference in sync; if this PR lands first, note in its own follow-up that the reference generator will need to pick these up.Requirements
packages/gittensory-miner/lib/that currently resolveGITHUB_TOKENand the coding-agent credential env vars (perMINER_CODING_AGENT_PROVIDER: an API-key env var, or an auth-file path for claude-cli/codex-cli), mirrorsrc/selfhost/load-file-secrets.ts's pattern.<NAME>_FILEvariable (e.g.GITHUB_TOKEN_FILE) in addition to the plain<NAME>variable.<NAME>_FILEis set and the plain<NAME>is unset, read the file's contents (trimmed of trailing whitespace/newlines) and use that as the effective credential value.<NAME>and<NAME>_FILEare set, follow the same precedence rule asload-file-secrets.ts(state explicitly, in code comments and docs, which one wins) rather than silently picking one.<NAME>_FILEis set but the file is missing or unreadable, fail with a clear, actionable error identifying the file path — never silently fall through to an empty/undefined credential.GITHUB_TOKENand to whichever credential(s) the currently-configuredMINER_CODING_AGENT_PROVIDERrequires._FILEalternative (e.g. mounting a Docker/Swarm/K8s secret at/run/secrets/<name>) alongside the existing plain-env-var example.Deliverables / Acceptance Criteria
<NAME>_FILEresolution implemented forGITHUB_TOKENand the active provider's credential(s)<NAME>and<NAME>_FILEare set_FILEpath (no silent fallback)_FILEexampleTest Coverage Requirements
This PR must ship with full test coverage for every changed line and branch — the repo's Codecov patch gate requires 99%+ coverage and the house standard is to aim for 100%, including both sides of every conditional/nullish-coalescing branch introduced. Add: (1) unit tests covering the new/changed logic's success and failure paths (plain var only,
_FILEonly, both set, neither set, file set but unreadable/missing, file set but empty), (2) an invariant test asserting the resolver never logs or returns the raw secret value anywhere in its output/log calls — only a boolean/source indicator — and (3) not applicable as a bug-fix regression test since this is net-new functionality, but include a test that an operator's existing plain-env-var setup keeps working unchanged (no regression to today's behavior).Codecov visibility note:
packages/gittensory-miner/**,apps/gittensory-miner-ui/**, andapps/gittensory-miner-extension/**currently sit entirely outside vitest'scoverage.includeglob, socodecov/patchcannot measure changes there yet — closing that gap is exactly what #4864 and #4865 (in this same milestone) do. Any part of this change underpackages/gittensory-engine/src/**or the repo's ownsrc/**remains fully Codecov-instrumented as usual. Either way, this does not lower the bar: treat the 100%-including-invariants-and-regression target above as the enforced house standard regardless of what Codecov can currently see, and it becomes gate-enforced for real once #4864/#4865 ship.Expected Outcome
An operator running AMS in fleet mode (Docker Swarm/Kubernetes) can mount
GITHUB_TOKENand coding-agent credentials as managed secrets instead of passing them as plaintext env vars visible viadocker inspect.Links & Resources
src/selfhost/load-file-secrets.ts— the pattern being portedpackages/gittensory-miner/lib/— where credential resolution needs the new indirection