gha-container-proof
gha-container-proof is the GitHub Actions job container and Docker action compatibility oracle for offline CI. It turns jobs.<job_id>.container definitions, steps[*].uses: docker://... references, and local action manifests with runs.using: docker into deterministic receipts with stable check IDs.
It is part of Wildmason's offline GitHub Actions proof-tool lane:
gha-workflow-proofchecks workflow structure.gha-eventsmithcreates event/context fixtures.gha-expression-proofevaluates Actions expressions.gha-cache-proofmodelsactions/cache.gha-artifact-proofmodels artifact upload/download.gha-service-proofmodels service containers (jobs.<id>.services) and readiness.gha-container-proofmodels the primary job container and Docker action containers.gha-github-service-proofmodels the GitHub API/service surface.gha-runner-image-proofvalidates runner-image manifests and the toolcache.gha-command-proofchecks runtime command and environment-file side effects.
gha-container-proof does not duplicate gha-service-proof. If a workflow has both job containers and services, check-workflow classifies the job-container side and emits container.services.delegated pointing at the sibling tool.
Install
cargo install gha-container-proof --locked
Commands
check-workflow
Scan workflow YAML for job containers and Docker action surfaces.
gha-container-proof check-workflow `
--repo . `
--workspace . `
--workflow .github/workflows/ci.yml `
--format json `
--output target/container-proof.json
When --workflow is omitted, the command scans .github/workflows/*.yml and .github/workflows/*.yaml. The scan detects:
jobs.<job_id>.containeras a bare image string or full object (image,credentials,env,ports,volumes,options).steps[*].uses: docker://image.steps[*].uses: ./local-actionwhoseaction.ymldeclaresruns.using: dockerwithimage: Dockerfile,image: docker://..., or a relative Dockerfile path.- Remote action refs (
owner/repo@ref) are classified as simulated — external action manifest unavailable unless mirrored or provided.
plan-job
Classify a concrete rendered job-container request.
plan-action
Classify a concrete Docker action request.
Also supports docker:// action references without a local path:
probe
Optionally use the Docker CLI to verify runtime availability and image behavior. Offline by default — does not pull images unless --allow-pull is set.
docker image inspect <image>records local image availability.docker run --rm <image> <tool> --versionanddocker run --rm <image> sh -c "<command>"record probe evidence.- Captures command kind, image, exit code, stdout/stderr excerpts, and elapsed time.
- Skips cleanly when the Docker CLI is not on PATH (
probe.docker_cli_not_found) and fails clearly when the daemon is unreachable (probe.docker_daemon_unreachable). - Override the Docker binary with
--docker-bin <PATH>or theGHA_CONTAINER_PROOF_DOCKERenvironment variable (used by tests to run against a fake docker).
What It Models
Job containers
- Container jobs require a Linux Docker runner; Windows and macOS hosted runners are unsupported.
- When a job runs in a container, shell steps execute inside the job container; networking is Docker-internal.
container.credentialsis recognized and classified as requiring secret-safe handling; credential values are redacted in receipts.container.optionsare parsed enough to flag known unsupported or dangerous options.--network/--netare unsupported under ci-forge-managed networking and emit a failure.--privileged,--pid=host,--ipc=host,--security-opt, and Docker socket mounts are flagged as risky.- Absolute Windows host paths (
C:\...,D:\...) mounted into Linux containers warn. --user,--workdir/-w,--entrypoint,--env/-e,--volume/-v,--cpus,--memory/-mare classified explicitly.- Unknown Docker options are classified as
simulated/unknownrather than silently accepted.
Docker actions
runs.using: dockeractions run in a new container for the action.runs.image: Dockerfile(or a relative*.Dockerfilepath) requires build support.runs.image: docker://...requires image availability.runs.pre-entrypoint,runs.entrypoint, andruns.post-entrypointare recognized.runs.argsis preserved.- Action
INPUT_*env can be passed through for receipts; the executor (ci-forge) owns post-state (STATE_*) handoff. - Dockerfile-backed actions detect missing Dockerfile, unsupported context, and build requirement.
Unsupported and warning surfaces
- Job container on non-Linux runner → fail.
- Docker unavailable on probe → skip (
probe.docker_cli_not_found) or fail (probe.docker_daemon_unreachable). - Image missing locally while offline → fail unless
--allow-pullis set. --network/--netin options when ci-forge owns networks → fail.--privileged→ warn (or fail under--strict).- Host Docker socket mount → warn (high-risk; explicit compatibility).
- Absolute Windows host mount into Linux container → warn.
container.credentialsvalues → redacted; absence checked.- Unknown Docker options → warn (
container.options.unknown). - Remote Docker action manifest unavailable → simulated (
action.manifest.unavailable).
Receipt shape
Every command emits the same top-level receipt:
Each subject describes one job container, Docker action, or Docker probe:
See docs/spec.md for the protocol surface and docs/RULES.md for stable check IDs.
GitHub Action
- uses: wildmason/gha-container-proof@v1
with:
command: check-workflow
repo: .
workflow: .github/workflows/ci.yml
format: json
output: target/container-proof.json
The action installs the crate with Cargo unless gha-container-proof is already on PATH.
Exit behavior
The CLI exits 0 when there are no failed checks. With --strict, warnings also fail the run.
Limits
gha-container-proof is a compatibility oracle, not a runner. It does not start long-lived job containers as the source of truth for execution — ci-forge owns the Docker lifecycle and attaches these receipts beside gha-service-proof, gha-cache-proof, gha-artifact-proof, gha-github-service-proof, and the rest.
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.