# Run bay without a runner

> A small team can merge with bay from its own machines, with no runner to host or keep running.

Source: https://getbay.io/docs/no-runner (HTML).

A small team can use bay with no runner at all. `bay merge --wait` runs the whole merge on the machine that asks for it. It builds the candidate, runs the checks, and pushes the result to main. Nothing else has to stay on, and your clone stays on your own machines.

This suits a few people with a few changes a day. A fleet of agents that should submit and leave still needs a runner.

## When to use it

- **A small team**: two or three people, each merging their own work from their own machine.
- **A private repository**: you would rather not clone it onto a shared machine.
- **A first trial**: try bay before you decide where a runner should run.

## Merge from your machine

Run `bay merge --wait` from the change's view. It returns the real outcome: merged, refused with a reason, or failed. Checks run in the sandbox on your machine: `bwrap` on Linux, `sandbox-exec` on macOS. Without a key registry, the merge is unsigned, and results say `trust: unsigned`. Leave `[runner] command` out of `.bay/config.toml`. With it set, `bay merge --wait` first submits to that command, and fails when the command cannot take it.

```sh
bay merge --wait
```

> While main is held, `bay merge --wait` returns `queued` with `MAIN_HELD`, and merges nothing.
> Run it again once the hold clears. A fix or an undo merges during a hold.

## Save edits from your main checkout

`bay save <intent>` merges the edits in your main checkout, the folder you cloned, as one change. It suits a person who edits files in place, with no `bay start` and no view. It takes every changed, new and deleted file, except files Git ignores. It opens a change with those files as its paths, and never commits on your branch. Then it runs `bay merge --wait`, so it merges the same way, with or without a runner. After the merge, your checkout fast-forwards to the new main when nothing is in the way.

```sh
bay save "fix the footer" --message "Footer links fit on a phone"   # --message is optional
```

> The intent says what the work is, in a few words, or gives a card id.
> `--message` sets the first draft's message. The commit on main keeps bay's own message.
> A save with words whose change already merged opens a new change for them.
> If your checkout is off the main branch, or files changed during the save, it stays where it was.
> Then `detail.checkout` says why, and main already holds the saved edits.

### When bay refuses a save

A refused save leaves every file in your checkout exactly as it was. When it opened a change, the result names it under `change`, and the change stays open. Run `bay save` again with the same words to go on. It reuses that open change. A change awaiting approval returns `APPROVAL_PENDING` and `detail.queued`. Once approved, a runner merges it; without one, save again. When main changed the same lines, it refuses with `MERGE_CONFLICT`. Bring those files up to main, then redo your edits.

- It runs only in the main checkout, never in a change's view or another worktree. In a view, run `bay merge`.
- It refuses while a merge, rebase, cherry-pick or revert is under way.
- It refuses a file with an unresolved conflict, and a submodule.
- It refuses when your `HEAD` holds a commit that main lacks.
- Each of these fails before bay opens a change, and changes nothing.
- With nothing to save, it returns `ok` and merges nothing.

## If your machine sleeps

The push to main is one atomic step, so main never gets half a merge. When the machine wakes, `bay merge --wait` reads the change's record and goes on. If main moved, it checks again. If its own lease ran out, it takes it back. If it slept after the push, bay reads the first result and never merges twice. Until the machine wakes, the change waits, unless a runner also runs, which may finish it for you.

## Approvals without a runner

An approval counts only with a key registry and an enrolled approver key. With `[policy] signing = "approvals"` in `.bay/policy.toml` on main, only approvers need keys. Then merging machines need none, and merges and check records run unsigned. Keys and the registry explains this mode. Require a person's approval shows how a person approves.

## When to add a runner

- **Agents should leave**: a runner finishes each merge after `bay merge` returns at once.
- **Machines sleep or move**: an always-on runner keeps merges going.
- **More changes a day**: the throughput settings need runners to run checks side by side.
- **Targets that follow main**: only `bay runner run` moves a target that sets `advance = true`.

> Runners shows how to run one on a machine or in your CI.

## Limits

- An operation's lease lasts `[runner] lease_s`, 60 seconds by default.
