Skip to main content

out-of-band

Signed receipts for CI checks that GitHub cannot run, or that already ran somewhere else.

PyPI Release CI Apache-2.0

English | 中文

Take the guided tour →

Two reasons to sign a receipt

GitHub cannot run the job. The code needs a particular accelerator, a licensed toolchain, a database reachable only from the office network, or a lab box several people share. A hosted runner cannot get there.

The job already ran somewhere else. Re-running it in CI is slow, flaky, or returns a different number. You want the gate to accept the run you have.

Both cases leave the same gap: a required check that no workflow can produce honestly. Out-of-band closes it with a statement the machine can sign without a GitHub credential and without an inbound path.

Putting a self-hosted runner on the machine is the usual alternative. It means a long-lived daemon on shared hardware, a credential that can pick up jobs, and repository code executing on the box. Uploading logs leaves the decision to someone reading a browser tab. Skipping the check turns the gate into a suggestion.

How it works

operator host                          GitHub
─────────────                          ──────
run ──► evidence ──► sign              PR comment / dispatch
                        │                    │
                        └── signed block ────┴──► verify.yml
                                                    │
                                                    ├── check run
                                                    ├── step summary
                                                    └── sticky comment
  1. The operator runs the job where it has to run and signs one statement: this repository, this commit, this tree, this job, this result, this evidence digest.
  2. The statement travels as a pull request comment, a dispatch input, or a file.
  3. A workflow on GitHub verifies the signature against .github/allowed_signers on the default branch, checks the bindings, and publishes a check run.

The repository owns the mechanism: format, signing, verification, publishing. The consumer owns the policy: which paths need a receipt, the job name, which key, and whether the check is required.

Four states, never collapsed

Receipt Verifier state Check conclusion
valid, result: pass verified success
valid, result: fail failed failure
absent or invalid unverified failure
not required by policy not applicable success

unverified means the gate could not tell. It publishes a failure, so a required check stays closed, and the summary carries the error code. A missing receipt is never a skip.

Quickstart

Requires Python 3.10+ and OpenSSH 8.0+ (ssh-keygen -Y sign). No runtime dependencies.

pip install out-of-band

Generate a key on the machine that runs the job. It never leaves that machine.

ssh-keygen -t ed25519 -N "" -C "ci operator" -f ~/.ssh/out-of-band

Commit the public key in the consumer repository as .github/allowed_signers:

operator ssh-ed25519 AAAA...

Sign one commit. --commit must be a full SHA that exists in the local clone; the tree is read from the commit, not the worktree.

oob sign \
  --job smoke \
  --commit "$(git rev-parse HEAD)" \
  --result pass \
  --evidence-dir ./evidence \
  --key ~/.ssh/out-of-band \
  --repo owner/name \
  --out receipt.md

That writes one block: an HTML marker, one line of JSON, and an SSH signature. Post it as a pull request comment, or pass it to a workflow_dispatch input. For a readable comment, run oob render --block receipt.md first.

Verify locally before posting:

oob verify --block receipt.md \
  --allowed-signers .github/allowed_signers \
  --repo owner/name --commit "$(git rev-parse HEAD)" --expect-job smoke

Exit codes: 0 valid pass, 1 valid fail, 2 absent or invalid, 3 tool error.

Use it in a repository

Two carriers, one verifier. The action runs as a step; the reusable workflow runs as its own job.

  verified:
    needs: policy
    runs-on: ubuntu-latest
    permissions:
      contents: read
      checks: write
      pull-requests: write
    steps:
      - uses: Oaklight/out-of-band@<full-sha> # v0.5.0
        with:
          pr: ${{ needs.policy.outputs.pr }}
          commit: ${{ needs.policy.outputs.commit }}
          job: smoke
          required: ${{ needs.policy.outputs.requires }}
          check-name: smoke-verified
          trusted-authors: operator-login

The snippet shows the gate wiring. Evidence is required by default, so add evidence-url or evidence-file, or set require-evidence: false.

Then require smoke-verified in branch protection. The check name is public API: renaming it silently changes what the branch protects.

Pin uses: by full commit SHA. A tag can move.

Evidence

Evidence is required by default. Supply the archive in one of three ways:

Source Use when
evidence-file the archive is already on the runner
evidence-url the archive is at an HTTPS address
claims.evidence_url the receipt should carry its own location
oob pack --evidence-dir ./evidence --out evidence.tgz

The verifier extracts the archive, recomputes the digest, and compares it with the value that was signed. A mismatch is E_EVIDENCE_MISMATCH and the verdict is unverified. A matching archive is uploaded as the oob-evidence artifact, and the job summary shows a manifest plus bounded text previews.

Set require-evidence: false to accept the signed digest without an archive.

The GitHub token is sent only to api.github.com and github.com. Other hosts must serve a public or signed URL.

What a receipt proves

That the holder of a listed key signed these exact bytes, binding one repository, commit, tree, job, and evidence digest.

It does not prove that the job ran correctly, that the evidence matches the digest, or that the host was uncompromised. It is an attestation, not a proof of correctness. See Trust and SPEC.md §8.

Documentation

Start with the docs map. The short version:

If you want to Read
Install and sign the first receipt Get started
Understand receipts, trust, evidence, states Concepts
Look up a command or an input CLI, action, workflow
Follow an end-to-end setup Patch gate, hardware run
Run it on an air-gapped host Air-gapped hosts
Compare with other tools Prior art

Chinese documentation mirrors the English tree under docs/zh/.

Install

pip install out-of-band

Each release also carries oob-<version>.pyz, a single-file build that needs only Python. It is the usual choice for a host that cannot reach PyPI.

Python 3.10 through 3.14 are tested. Windows works with python oob-<version>.pyz.

Project

Non-goals

No runner, no hosted service, no GitHub App, no Sigstore, no transparency log, no artifact storage. The tool signs and verifies a statement; everything else belongs to the repository that uses it.

Metadata

Release files for out-of-band 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for out-of-band 0.5.0
File Size Uploaded Provenance
out_of_band-0.5.0.tar.gz 36.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for out-of-band 0.5.0
File Interpreter ABI Platform
out_of_band-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 66.4 kB

Release files / out_of_band-0.5.0.tar.gz

Download URL out_of_band-0.5.0.tar.gz
Size 36.1 kB
Tags Source
SHA-256 checksum
How to use checksums
5a9d46a09fff2d40a2d94738b212c02a6f63e0df3bb5bda928fd8fe520dbfc48
BLAKE2b-256 checksum
How to use checksums
be49eeaec3cc56aec160f9f01b9e8e03d74403a0700062241c6f645ea4e22492
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Release files / out_of_band-0.5.0-py3-none-any.whl

Download URL out_of_band-0.5.0-py3-none-any.whl
Size 30.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
dd702abfdd7c573027c0af7ee9aa19ae43506264b3f04fac7b49bec74fd67331
BLAKE2b-256 checksum
How to use checksums
026d94425544c157aaf4ca257bb81e6766e5e59177aa0932d6e0789943a8ed20
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Security for out-of-band 0.5.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for out_of_band-0.5.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page