# Build on bay

> Build your own product or internal platform on bay's public contracts, the way tools build on Git.

Source: https://getbay.io/docs/build-on-bay (HTML).

bay is a tool, like Git, and anyone can build on it. A platform team can wrap it for many repositories. A tool maker can show its state in new screens. A hosting provider can serve repositories whose pushes bay judges. Each of them calls bay's commands, reads its results and plugs in through its plugins. bay stays one program for all of them, with nothing special for any one builder.

This page is a map. It names each contract and the page that holds it. bay has no server, no accounts and no screens. Those belong to your product.

## When to use this page

- **An internal platform**: your team runs bay across many repositories and wants one view of them.
- **A repository host**: you serve main to others, and bay's hooks judge every push.
- **A tool for agents or people**: you show changes, refusals, locks and approvals in your own screens.
- **A runner farm**: you run checks and merges on your own machines, CI or containers.

## The contracts

Each row names a contract, what a product does with it, and where these docs describe it. Read that page for the details. This table only points to it.

| Contract | What your product does with it | Where it lives |
|---|---|---|
| `bay.result/1` on stdout, with `--json` | Reads every outcome: `decision`, `reason`, `allowed`, `retry_when` and `next_step`. | Results and refusals: The result document |
| `bay help --json` | Reads the commands, flags, reason codes, word list, agent hooks and result schema of the bay it runs. | Results and refusals: Read the contract from bay |
| `say` and `detail.words` | Shows people bay's plain sentence, and keeps bay's inner words out of its own screens. | Results and refusals: The plain sentence |
| `bay host` and its Git hooks | Serves main from a bare repository whose own hooks judge each push. | Host a main branch without a forge |
| The key registry, `refs/bay/registry`, and `bay enrol` | Decides which agent, runner, producer, reviewer and approver keys count. | Keys and the registry |
| `[runner] command` and `bay runner run --once` | Runs merges on its own machines, CI or containers. | Runners: The external runner contract |
| `bay status --since` and `detail.cursor` | Follows merges and events after a cursor, and stores the cursor between reads. | See what your fleet is doing: Read what changed |
| `bay status --all` | Reads the fleet map: machines, sessions, runners and red checks. | See what your fleet is doing: What the map shows |
| `[plugin.notify]` and `bay.event/1` | Gets each event the runner writes, once. `bay status --since` still lists one it lost. | See what your fleet is doing: Get notices |
| `[plugin.wake]` and `bay.wake/1` | Wakes an agent when a talk waits for it. | Talk to other agents: Wake the recipient |
| `[plugin.assets]` and `.bay-assets` | Keeps large files in its own store, named by SHA-256 in Git. | Configuration: Keep large files out of Git |
| Main's commit trailers: `Change-Id`, `Actor`, `Open:` | Shows who merged what, in the agent's own words, and what waits for a person. | Concepts: What main's commit says |
| `[locks] paths` | Names files that never merge, so one session at a time edits each. `bay status --all` lists the locks held now in `detail.locks`. | Agents and hooks: Lock files that never merge |
| `bay waiting`, `bay approve` and `bay reject` | Puts a person's yes or no in its own screens. | Require a person's approval |
| `bay evidence record` | Records a review from its own system, signed with a reviewer key. | Wire any reviewer to bay |
| `detail.agents` | Knows what bay judges in each agent product, and what it cannot see. | Agents and hooks: Read the hooks from bay |
| `bay plugin test` | Runs bay's contract cases against each plugin it ships. | Configuration: Test a check or a plugin |

## Host main and judge every push

Run `bay host` on the machine that serves your repositories. Its hooks are Git's own, set in the repository's config, so `git receive-pack` runs them on every push. SSH and Git's own HTTP backend both run that program, so both reach bay's hooks. Who may push at all stays with your product: SSH keys, tokens or file permissions. Until the host holds a key registry, it only keeps main moving forward. Create the registry before anyone else pushes, so the host also checks each push's signatures and scope.

```sh
bay host /srv/git/app.git --main main
```

> bay's hooks are `hook.bay-trunk` and `hook.bay-scope`. Add your own under another name.
> A `reference-transaction` hook, for example, sees only the refs that bay's hooks let through.
> Use one to copy what bay accepted to a second store.

## Run runners where you like

A runner needs a clone, the checks' tools and, with a key registry, its own `runner` key. It can loop, or make one pass with `--once` whenever your product calls it, such as after a push. Read the pass's `decision`, and let `retry_when` say whether another pass can help. `by_itself` and `after_wait` can; `after_person` and `after_fix` wait for someone. A pass with no answer is unknown, and unknown is never success.

```sh
bay runner run --once --json
```

> A runner never holds or uses an agent key.
> Keep its own key where no agent session can read it.

## Give people plain sentences

Show `say` as bay wrote it. It says what happened, then why, then one step. Its words never include a word that `detail.words` lists. Use the same list in your own screens, so a person meets one word for each idea.

```sh
bay help --json | jq '.detail.words[] | {word, say, avoid}'
```

## Read state through bay

`bay status` does not run in a bare repository, so keep a plain clone beside your host. Read state there with `bay status --json`. `bay status --since <cursor>` lists what happened after the cursor you stored. The cursor is opaque. After `bay status`, `bay status --all` lists each live session's agent, change and state. The records under `refs/bay/*` are bay's own. Read them through bay, not by parsing them.

```sh
bay status --since --json             # the first read: take detail.cursor
bay status --since "$cursor" --json   # every read after: what happened since
```

## Store large files through a plugin

An assets plugin is one program with `put`, `get` and `exists`. It can talk to any store you run: a folder, a bucket or your own service. bay hashes every file itself, so it never trusts the identity a store names. A change merges only when the store holds its bytes.

```sh
bay plugin test assets -- scripts/bay-assets
```

## Sign with the key registry

Give each agent, runner and producer its own key, and enrol only the public half. Keep the registry key with a person, for example on a hardware key. Then your product never holds a key that can add a key. A removed key stays out: `bay enrol` refuses it, and the host refuses a registry version that drops a removal.

```sh
bay enrol agent agent-7 agent-7.pub
```

## Pin the bay you build on

bay's version numbers are 0.x, so a minor release can still change a command. The changelog names each change. Pin one exact version: `BAY_VERSION` at install, and `bay.updateVersion` for updates. Move the pin on purpose. After each move, read `detail.commands` from `bay help --json`, and run `bay plugin test` again. A consumer refuses a major schema version that it does not know.

```sh
curl -fsSL https://getbay.io/install.sh | BAY_VERSION=<version> sh
```

## What bay does not do

- It runs no server and keeps no accounts.
- It has no web pages and no app. At a terminal, it prints plain sentences.
- It does not decide who may connect to push or clone. SSH, your Git server or your product does.
- It sends no telemetry, and your code goes only to your own Git remote.
- It has no build or option for one builder.
- What a product needs goes in its own config, plugins and docs.
