# Agents and sandboxes

Coding agents run the same checks over and over. With hushrun, an agent skips every check that you, CI or another agent already passed on the same files, and its own passes let CI skip them in turn.

## Agents on your machine

An agent that works on your laptop, such as Claude Code, Codex or Cursor's agent, runs your scripts the way you do. `npm test` goes through `hushrun run`, uses your sign-in, and records as you. There is nothing to set up.

What an agent should know about working in a repository with hushrun, such as reading a skip as a pass, is in [For coding agents](https://hushrun.com/docs/for-agents.md), with a snippet for your `AGENTS.md`.

## Agents in sandboxes

An agent in a sandbox, such as a Claude Code cloud session, `claude -p` in a container, or an E2B or Docker sandbox, has no browser to sign in with. It uses an agent token instead: one an organization admin makes, which records and reads results for one repository, as `agent:<name>`, until it expires or someone revokes it. It can do nothing else. Every token comes with a key, the agent's machine, and hushrun's server records only results that key signed.

1. **Create a token.** On your organization's **Agents** page in the dashboard, or in the repository, signed in with `hushrun login`:

   ```bash
   hushrun token create --agent fleet-a
   ```

   It shows the token and the agent's key once. The server keeps only the token's hash and the key's public half; the dashboard makes the key in your browser, and keeps no copy. One token and its key can serve a whole fleet of sandboxes.

2. **Give each sandbox three variables:**

   ```dotenv
   HUSHRUN_TOKEN=hra_…
   HUSHRUN_MACHINE_KEY="-----BEGIN PRIVATE KEY-----
   …
   -----END PRIVATE KEY-----"
   HUSHRUN_PROVIDER=local
   ```

   `HUSHRUN_MACHINE_KEY` is the key shown with the token: the whole PEM block, as one value. A sandbox that keeps the key in a file outside the repository sets `HUSHRUN_MACHINE_KEY_FILE` to its path instead. Without the key, checks still run, and still skip on others' passes, but their results aren't recorded. `HUSHRUN_PROVIDER=local` matters where the sandbox sets `CI=true`, so that hushrun doesn't take it for a CI job.

3. **Let it reach** `api.hushrun.com` and your package registry.

When `package.json` names your organization's backend on hushrun's hosted service, as init writes it, that's all. `npm test` in the sandbox skips what any agent, teammate or CI job already passed on the same files, and records the rest. `hushrun whoami` shows the agent, its repository and when its token expires:

```console
$ hushrun whoami
backend   https://api.hushrun.com/o/acme (this repository's package.json names it)
token     HUSHRUN_TOKEN, an agent token
as        agent:fleet-a in acme
agent     fleet-a (hra_AbC123…)
for       github.com/acme/shop
expires   2026-11-26 15:00 UTC (in 30 days)
limit     300 results a minute
dashboard https://app.hushrun.com/acme
…
```

### What a token covers

| Option of `hushrun token create` | Default |
|---|---|
| `--repository github.com/acme/shop` | The repository you run it in |
| `--org-wide` | Every repository of the organization, instead of one |
| `--expires 90d` | 30 days; from 1 hour to 365 days, such as `12h`, `30d` or `8w` |
| `--rate 1000` | The most results it records a minute: 300 on hushrun's hosted service |

The key comes with every token, whatever the options. It signs the agent's results, and its [promotion evidence](https://hushrun.com/docs/promotion/evidence.md) too. `hushrun token list` shows the organization's tokens, and `hushrun token revoke` revokes one, with its key, by its prefix, or by its agent's name when only one token has it.

### Claude Code cloud sessions

In the environment's settings:

- the environment variables `HUSHRUN_TOKEN=hra_…`, `HUSHRUN_MACHINE_KEY`, the key's whole PEM block, and `HUSHRUN_PROVIDER=local`;
- network access that reaches `api.hushrun.com`, as an allowed domain or with full access, and the npm registry;
- a setup script that installs the dependencies, such as `npm ci`, which installs hushrun with them.

Each session's `npm test` then goes through `hushrun run`, and skips what another session, a teammate or CI already passed on the same files.

### A container

Any sandbox takes the same variables, whether through `docker run -e`, an E2B sandbox's environment or a Kubernetes job's `env`:

```bash
docker run --rm \
  -e HUSHRUN_TOKEN -e HUSHRUN_MACHINE_KEY -e HUSHRUN_PROVIDER=local \
  -e HUSHRUN_INVOKED_BY=agent:worker-1 \
  -e ANTHROPIC_API_KEY \
  agent-image sh -c '
    git clone https://github.com/acme/shop && cd shop && npm ci &&
    claude -p "Fix the flaky cart test. Run npm test before you commit."'
```

## Fleets

- **One token, a label each.** `HUSHRUN_INVOKED_BY=agent:<id>` labels each sandbox's runs, so the dashboard tells a fleet's members apart. The token still decides who recorded.
- **One machine per token.** Every sandbox signs with the token's key, so the organization's machines list shows one machine for the token, however many sandboxes use it.
- **A limit per token.** A token records at most so many results a minute, so a runaway fleet can't flood your organization. Past it, checks still run and earlier passes still skip; only the new result isn't recorded.
- **Rotation.** Tokens expire. Create the next one under the same agent name, which comes with a new key, switch both variables, then revoke the old one.

## Another server

An agent token follows `package.json`'s backend only on hushrun's hosted service, `https://api.hushrun.com`, so a change to `package.json` can't send it anywhere else. For a backend on another server, set `HUSHRUN_BACKEND` to the organization's URL in the sandbox too, or `HUSHRUN_HOST` to that server.

## When a sandbox's results aren't recorded

hushrun says why in one line, and runs the check anyway:

| Line | What to do |
|---|---|
| `test running: acme refused HUSHRUN_TOKEN: agent token hra_AbC123 (agent:fleet-a) expired on 2026-10-20` | Create another token under the same name, and update the variable |
| `… was revoked on …` | The same, if the sandbox should still record |
| `test passed in 41s; not recorded: agent token hra_AbC123 records results for github.com/acme/shop only, and this result is for github.com/acme/web` | The token belongs to another repository: use that repository's token, or one made with `--org-wide` |
| `test passed in 41s; not recorded: agent token hra_AbC123 (agent:fleet-a) recorded 300 results in the last minute, its limit` | The fleet records faster than its token allows: slow it down, or create the token with a higher `--rate` |
| `test passed in 41s; not recorded: …; upgrade: https://app.hushrun.com/acme/-/billing` | Your organization recorded every check its plan includes this period. See [Plans and limits](https://hushrun.com/docs/plans.md) |
| ``test passed in 41s; not recorded: set `HUSHRUN_MACHINE_KEY` to the key `hushrun token create --agent` printed`` | The sandbox has no key, or not the one its token came with. Set `HUSHRUN_MACHINE_KEY` to the key shown with the token; if nobody kept it, create a new token |
| `HUSHRUN_TOKEN isn't sent to …` | The backend isn't on hushrun's hosted service: set `HUSHRUN_BACKEND` or `HUSHRUN_HOST` |

A pass an agent records skips the check in CI, as a laptop's does, unless the repository has [CI reuses only CI's results](https://hushrun.com/docs/ci.md#ci-only) turned on.
