Evidence from agents and laptops
A coding agent, or a person, can run a stage's checks before pushing, in the image CI runs them in, on exactly the files about to be committed, and sign what happened. CI's plan then counts that evidence and skips those checks.
To skip checks with hushrun run -- COMMAND alone, an agent token is all a sandbox needs: see Agents in sandboxes. This page is about promotion, and assumes the pipeline is set up (Promotion).
How it works
hushrun run --all --stage staging --isolated --worktree--worktreedigests the files asgit add -A && git commitwould record them now: the evidence is about the commit about to be made.--allruns every source check the stage requires that has no valid evidence yet.--isolatedruns each check in its pinned image and platform, with Docker or Podman, on an export of exactly the digested files. The check gets no network unless it declares one, and only the variables itsenvnames: never a secret, and never the signing key, which stays with hushrun.- The evidence records custody
isolated, with the image, platform and network it ran with. CI's plan counts it at every stage that isn't CI-only.
Then commit exactly what was validated, with no other change, and push. If the files differ from what ran, the digest differs, and CI runs the checks itself.
Pin the images CI runs checks in
An isolated run needs the check's image, pinned by digest, and its platform. A check that pins them counts evidence only from that image and platform, so pin the image your CI job already uses:
checks:
unit:
kind: source
run: go test ./...
image: golang@sha256:<digest of the image CI uses>
platform: linux/amd64
env: [GOFLAGS] # the only variables the check sees
network: none # the defaultnetworkisnoneby default.openis for a check that downloads.restrictedruns the check on the Docker or Podman network thatHUSHRUN_RESTRICTED_NETWORKnames, one whose egress you restrict.- CI must run the check in the same image. With the reusable workflow on GitHub, set
isolated: true: its test job then runs the plan's checks withhushrun run --isolatedtoo. Otherwise run the CI job in that image, and pass it tohushrun run --stageas--environment-image <image>@sha256:…. - A check whose
envnames a secret never runs isolated: keep it in CI. hushrun refuses any name that containsTOKEN,SECRET,PASSWORD,PASSWD,PASSPHRASE,CREDENTIAL,PRIVATE,AUTH,ACCESS_KEY,APIKEY,DATABASE_URLor_KEY_, ends in_KEY,_PAT,_DSNor_PWD, or is one of a few more, such asGOOGLE_APPLICATION_CREDENTIALS,REDIS_URLandSSH_AUTH_SOCK. The list is a safety net: name inenvonly what the check needs.
Give each agent a key
An agent signs with the machine key its agent token came with. Its events count because the agent is one of the org's signers: nothing goes in hushrun.yaml. An org admin makes the token, which always comes with its key, in the repository or on the dashboard's Agents page:
hushrun token create --agent claude-codeIt prints the token, hra_…, and the machine key, once. Every sandbox of the agent signs as that one machine. Give each sandbox:
| Variable | Value |
|---|---|
HUSHRUN_ | The agent token |
HUSHRUN_ | The machine key: the whole PEM block, as one value. Or HUSHRUN_, the path of a file outside the repository that holds it |
HUSHRUN_ | local, in a sandbox that sets CI=, so that hushrun doesn't take it for CI |
HUSHRUN_ | Optional: agent:<name>, which events record. Nothing decides on it |
The sandbox also needs Docker or Podman. HUSHRUN_CONTAINER_RUNTIME picks one, or names another Docker-compatible CLI. Exports go under hushrun's cache directory, which Docker Desktop, Colima and Podman machines share with their VMs; HUSHRUN_EXPORT_DIR moves them.
- The token lasts 30 days unless
hushrun token create --expiressays otherwise, 365 days at most. The same token serveshushrun run -- COMMAND's results (Agents in sandboxes). - The key never goes in the repository: a key there would be part of what
--worktreedigests and the agent commits. hushrun refuses a key file inside the repository, and an isolated run refuses to export one. - Revoking the token, with
hushrun token revoke claude-codeor in the dashboard (Agent tokens), stops what the agent signs from then on from counting. What it signed before keeps counting. HUSHRUN_SIGNING=machine hushrun doctorsays whether hushrun's server counts the machine, and as whose.
People's machines
A teammate's machine counts the same way, with no token to make. hushrun login registers the machine it runs on, with a key hushrun makes the first time and keeps beside its credentials, never in a repository. Its events count while its owner is a member of the org, at every stage that isn't CI-only.
- On a signed-in machine, hushrun signs with that key by default, but never in CI: there it signs with a machine key only when
HUSHRUN_MACHINE_KEYorHUSHRUN_MACHINE_KEY_FILEgives one, so a person's sign-in left on a runner never signs a pipeline's events as that person. hushrun whoamishows the machine's key id.hushrun logoutrevokes the sign-in and the machine with it. What it signed before keeps counting.- A machine unused for 90 days is revoked, and
hushrun loginon it brings it back. A person has at most 20 machines in an org. - Checks that pin no image can also run on the machine itself, with
hushrun run --all --stage staging --worktree: that evidence records custodylocal.
The organization's machines, and how to revoke one, are in Machines.
Keep a stage to CI's evidence
A stage that should never count an agent's or a laptop's evidence, such as production, is made CI-only in the dashboard: for every repository of the org on the org's Settings page, or for one repository on its repository page. It isn't in hushrun.yaml, so no pull request, an agent's included, can drop it.
- At a CI-only stage, checks, artifacts and lifecycle events count only when the repository's CI signed them. Approvals still come from people.
- Evidence others signed before the stage became CI-only keeps counting there; evidence signed while it was CI-only never does.
- A GitLab pipeline signs as an agent, so don't make a GitLab project's stages CI-only (GitLab CI).
More in CI-only stages.
Tell the agent
Add this to AGENTS.md, or the file your agent reads, and adjust the stage:
## Before pushing
Run `hushrun run --all --stage staging --isolated --worktree`. It runs the missing checks in the
pinned CI image on exactly the content you are about to commit, and records signed evidence.
Commit everything it validated, with no other changes, then push. CI then skips those checks.Approvals
A stage that requires an approval check waits until someone approves the digest it would deploy. Until then its plan says blocked, and names the approval:
checks:
release: { kind: approval }
stages:
production:
branch: main
after: staging
require: [lint, unit, integration, release]From your own machine, signed in with hushrun login:
hushrun approve release --stage production --comment "QA signed off"- It shows what you approve, the digest and its revision, what the stage runs now, and the
git logrange between the two. Then it signs with your machine's key, and records the approval. - It checks first that the approval would count. If it wouldn't, it records nothing, and says why.
- Approvals count only from people.
hushrun approverefuses to run in CI or as an agent, and a plan never counts an approval the CI or an agent signed, at any stage: no pipeline or agent approves its own deploy. --out approval.jsonwrites the signed approval to a file instead of recording it, to upload in the dashboard.
In the dashboard, an approval is given on the digest's review page, reached from the overview's "waits for approval" and from the digest's page. Approving there registers your browser's key as your machine.
Then run the stage's plan again, such as by re-running its workflow: it deploys. A forced run never waives an approval.
When something is refused
| Message | What to do |
|---|---|
pins no `image`, so it can't run isolated | Pin the check's image, by digest (above) |
an isolated run never passes secrets in | Keep that check in CI, or drop the variable from its env |
declares `network: restricted`, which hushrun can't provide | Set HUSHRUN_ to a network whose egress you restrict |
isn't on PATH: install Docker or Podman | Install one, or set HUSHRUN_ |
collides with another digested path | Two paths differ only in case: set HUSHRUN_ to a case-sensitive directory |
here digests to …, not … | The checkout isn't the content named: run where it is, with - for uncommitted changes |
a machine's key must never be committed | Keep the key in hushrun's configuration directory, or set HUSHRUN_ outside the repository |
this command signs events | Sign in with hushrun login, or set HUSHRUN_ |
no valid signature by a machine of a member or an agent of the org | Sign in with hushrun login, or give the agent the key its token came with, in HUSHRUN_ |
this approval wouldn't count | Approve from a machine registered with hushrun login |
approvals count only from people | An agent or the CI tried to approve: a member approves on their own machine with hushrun approve, or in the dashboard |