# Evidence from agents and laptops

This guide lets a coding agent or a person run a deploy's checks before pushing, so the deploy's plan counts the results and skips those checks.

## Before you begin

- Set up the deploy: see [Deploys](https://hushrun.com/docs/deploy.md#start).
- Install Docker or Podman where checks run isolated. [Environment variables](https://hushrun.com/docs/reference/environment.md) has the settings for both.
- For `hushrun run -- COMMAND` alone, see [Agents in sandboxes](https://hushrun.com/docs/agents.md#sandboxes) instead.

## Run the checks before pushing

```bash
hushrun run --all --isolated --worktree
```

- `--worktree` digests the files as `git add -A && git commit` would record them now, so the evidence is about the commit you're about to make.
- `--all` runs every check the deploy requires that has no valid evidence yet.
- `--isolated` runs 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 its `env` names. It never gets a secret or the signing key.

The evidence records custody `isolated`, with the image, platform and network it ran with. The deploy's plan counts it, unless the repository [takes CI's evidence only](#ci-only).

Then commit exactly what ran, with no other change, and push. If the files differ, so does the digest, 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:

```yaml title="hushrun.yaml"
checks:
  unit:
    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 default
```

- CI must run the check in the same image. With the reusable workflow on GitHub, set `isolated: true`. Otherwise run the CI job in that image, and pass it to `hushrun run` as `--environment-image <image>@sha256:…`.
- `network: open` is for a check that downloads. `restricted` runs it on the Docker or Podman network that `HUSHRUN_RESTRICTED_NETWORK` names, one whose egress you restrict.
- A check whose `env` names a secret never runs isolated: keep it in CI. See [Names treated as secrets](#secret-names).

## Give each agent a key

An agent signs with the machine key that comes with its agent token. Its events count because the agent is one of the org's signers, so nothing goes in `hushrun.yaml`. An org admin makes the token, in the repository or on the dashboard's **Agents** page:

```bash
hushrun token create --agent claude-code
```

At a terminal, it shows one string, once, with the token and the key inside. In CI, or where stdout or stderr isn't a terminal, it doesn't show: `--format json` gives it to a script as `hushrun_agent`. Give each sandbox the string as `HUSHRUN_AGENT`. Every sandbox of the agent signs as that one machine.

- The token lasts 30 days unless `--expires` says otherwise, 365 days at most. The same token serves `hushrun run -- COMMAND`'s results.
- The key never goes in the repository, where `--worktree` would digest it. hushrun refuses a key file inside the repository, and an isolated run refuses to export one.
- Revoking the token, with `hushrun token revoke claude-code` or in the dashboard ([Agent tokens](https://hushrun.com/docs/teams.md#agent-tokens)), stops what the agent signs from then on from counting. What it signed before keeps counting.
- `HUSHRUN_SIGNING=machine hushrun doctor` says whether hushrun's server counts the machine, and as whose.
- `HUSHRUN_INVOKED_BY=agent:<name>` is optional: events record it, and plans ignore it.

A sandbox set up with `HUSHRUN_TOKEN` and `HUSHRUN_MACHINE_KEY` keeps working. See [Agents in sandboxes](https://hushrun.com/docs/agents.md#sandboxes).

## Sign in on people's machines

A teammate's machine counts with no token to make. `hushrun login` registers it, with a key hushrun keeps beside its credentials, never in a repository. Its events count while its owner is a member of the org, unless the repository takes CI's evidence only.

Checks that pin no image can run on the machine itself, with `hushrun run --all --worktree`. That evidence records custody `local`.

- `hushrun whoami` shows the machine's key id.
- `hushrun logout` revokes the sign-in and the machine. What it signed before keeps counting.
- A machine unused for 90 days is revoked, and `hushrun login` brings it back. A person has at most 20 machines in an org.
- In CI, hushrun signs with a machine key only when `HUSHRUN_MACHINE_KEY` or `HUSHRUN_MACHINE_KEY_FILE` gives one, so a sign-in left on a runner never signs as that person.

See [Machines](https://hushrun.com/docs/teams.md#machines) to list or revoke the organization's machines.

## Keep deploys to CI's evidence

An admin can turn on **CI reuses only CI's results** for the repository, so its deploys count no agent's or laptop's evidence. It isn't in `hushrun.yaml`, so no pull request, an agent's included, can turn it off. See [CI's evidence only](https://hushrun.com/docs/deploy.md#ci-only), and why a GitLab project shouldn't turn it on.

## Tell the agent

`npx hushrun init` writes [what agents read](https://hushrun.com/docs/for-agents.md#agent-files) and installs the pre-push hook. Both come down to one command:

```console
$ hushrun ready
a0b1c2d isn't ready: CI would run 1 check again.
  missing unit  no pass yet for this exact code
Run these here, then push:
  hushrun run --isolated unit
```

The hook refuses an agent's push with the same list. `hushrun ready` lists the deploy's checks only in a `hushrun.yaml` with a `deploy` block. A check nothing here can change stays with CI, and doesn't count against the code: one in a repository that takes CI's evidence only, one that needs a secret, or one pinned to an image on a machine with no Docker or Podman.

## If something is refused

| Message | What to do |
|---|---|
| ``pins no `image`, so it can't run isolated`` | Pin the check's image, by digest ([above](#images)) |
| `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_RESTRICTED_NETWORK` to a network whose egress you restrict |
| `isn't on PATH: install Docker or Podman` | Install one, or set `HUSHRUN_CONTAINER_RUNTIME` |
| `collides with another digested path` | Two paths differ only in case: set `HUSHRUN_EXPORT_DIR` to a case-sensitive directory |
| `here digests to …, not …` | The checkout isn't the content named: run where it is, with `--worktree` for uncommitted changes |
| `a machine's key must never be committed` | Keep the key in hushrun's configuration directory, or set `HUSHRUN_MACHINE_KEY_FILE` outside the repository |
| `this command signs events` | Sign in with `hushrun login`, or set `HUSHRUN_MACHINE_KEY` |
| `no valid signature by a machine of a member or an agent of the org` | Sign in with `hushrun login`, or give the agent its string as `HUSHRUN_AGENT` |

## Details

### Names treated as secrets

An isolated run refuses any `env` name that:

- contains `TOKEN`, `SECRET`, `PASSWORD`, `PASSWD`, `PASSPHRASE`, `CREDENTIAL`, `PRIVATE`, `AUTH`, `ACCESS_KEY`, `APIKEY`, `DATABASE_URL` or `_KEY_`;
- ends in `_KEY`, `_PAT`, `_DSN` or `_PWD`;
- is one of a few more, such as `GOOGLE_APPLICATION_CREDENTIALS`, `REDIS_URL` and `SSH_AUTH_SOCK`.

The list is a safety net: name in `env` only what the check needs.

## Next

- [The planner](https://hushrun.com/docs/deploy.md#planner): how a deploy decides what to do.
- [GitHub Actions](https://hushrun.com/docs/deploy/github.md): run checks in CI in the same images, with `isolated: true`.
- [For coding agents](https://hushrun.com/docs/for-agents.md): what agents read and do before a push.
