Skip to content
bay
How it worksFleetRoadmapDocsBlogGitHub ↗
Early access
Docs menu

Start here

  • Overview
  • Install
  • Quickstart
  • Concepts

Land changes

  • Executors
  • Undo a landing
  • Configuration

Coordinate agents

  • Agents and hooks
  • See what your fleet is doing
  • Talk to other actors

Operate bay

  • Host a trunk without a forge
  • Keys and the registry
  • Update

Reference

  • CLI reference
  • Results and refusals
  • Changelog

Start here

  • Overview
  • Install
  • Quickstart
  • Concepts

Land changes

  • Executors
  • Undo a landing
  • Configuration

Coordinate agents

  • Agents and hooks
  • See what your fleet is doing
  • Talk to other actors

Operate bay

  • Host a trunk without a forge
  • Keys and the registry
  • Update

Reference

  • CLI reference
  • Results and refusals
  • Changelog

Coordinate agents

See what your fleet is doing

See every machine, actor and session on a repository, and read what changed since you last looked.

bay is in development. These pages describe bay as it will ship.

bay status --fleet shows what each agent on each machine works on. bay watch lists what changed since you last looked: landings, events and invalidations. Both read Git, so you need no server and no SSH between machines.

The fleet map shows your last fetch, not a live feed. It never contacts the remote itself, and it says how old its fetch is. A notice is a convenience. The executor log and trunk stay the record.

When to use the fleet map

  • Before you start: see who holds a change, and on which machine.
  • Find a stalled session: see each session's state and the age of its last report.
  • Catch up after a break: run bay watch with your last cursor and read what landed.
  • Feed a chat or a dashboard: point the notify adapter at it, and fill gaps with bay watch.

Show the fleet map

Run bay status first. It fetches trunk, then copies each machine's live ref into refs/bay/fleet/*. Then run bay status --fleet. It reads that copy and fetches nothing.

sh
bay status           # fetch trunk and the live refs
bay status --fleet   # read the map from that fetch

Where the map comes from

  1. 01

    Record

    Each agent hook event writes the session's state to a local work record.

  2. 02

    Summarise

    A hook event starts a background bay live push. It pushes at most once per heartbeat. It replaces refs/bay/live/<machine> with one commit that holds live.json, one row per live session. With a key registry, the agent key of that machine signs the commit.

  3. 03

    Fetch

    bay status fetches every refs/bay/live/* into refs/bay/fleet/*. It records when that fetch first brought each summary, by this machine's clock.

  4. 04

    Read

    bay status --fleet joins those summaries with this machine's own records. With a key registry, a summary that its named agent did not sign lists no sessions.

What the map shows

FieldMeaning
machinesEach machine in the fetch, with its age and its number of fresh sessions. An unreadable summary shows readable: false and why.
actorsEach actor the cache knows, the machines it is on, and its freshest age.
sessionsOne entry per fresh session: machine, actor, agent, change, state, last event, context use, last write and age.
sessions[].behindWhether trunk moved past the base of that session's change. null when this cache does not hold the change.
behindWhether the fetch is behind the remote: true once the fetch is one takeover window old, else null. Never false.
fetched_at, fetch_age_sWhen the fetch ran, and its age in seconds.
machines_truncatedtrue when the map left machines out.
talksAlways empty for now.

Untracked and unknown

A held change with no fresh session gets an entry of its own. Its view may be on this machine with no session record here that names it. Then its state is untracked. That change had no hooked session, for example one with hooks off. bay never shows it as idle. Its view may be on another machine. Then bay cannot judge it from here, and its state is unknown. A change whose hooked session here ended gets no entry.

Ages and clocks

  • bay never compares the clocks of two machines.
  • A row's age starts with the summary's age on your clock, from the fetch that first brought it.
  • bay adds the row's age inside the summary, by the clock of the machine that wrote it.
  • A row one takeover window old drops out of the map.
  • This machine's own sessions come from its local records, not from the remote's copy.

Watch for changes

bay watch fetches trunk and the executor logs, then lists what happened after a cursor. It lists trunk landings, bay.event/1 events from the executor logs, and new invalidations of your change. It returns detail.cursor. Pass it to --since next time to go on from there. The cursor is opaque. Use it as bay returned it.

sh
bay watch                    # the current state of this view's change
bay watch --since <cursor>   # what happened after that cursor

What watch lists

  • In a change's view, it lists only events that touch that change's inputs.
  • Those are events about the change itself, about the policy, or about an input it declared.
  • Outside a view, it lists every event and judges no change.
  • Without --since, a view gets its state first: landings since its base, its events and its invalidations.
  • Offline, it reads the local cache, and freshness says so.
  • bay watch only reads. It never writes the change record and never sends a notice.

Get notices

Set [notify] command in .bay/config.toml to a program in the repository. The executor runs it once after each event it writes, with the event's JSON on stdin. Exit 0 means delivered. Any other exit, or no exit within 5 seconds, means lost. bay never retries a lost notice. bay watch still lists the event. Each event carries an id, so the adapter can drop duplicates. The adapter gets only PATH and HOME from the executor's environment.

toml
[notify]
command = "scripts/notify"   # gets one bay.event/1 on stdin, exits 0 when delivered

What the map cannot do

  • It cannot ask the remote. It is as fresh as your last bay status.
  • It cannot see inside a session with hooks off. It shows that session's change as untracked.
  • It reads at most 256 machine summaries.

Refusals

bay status --fleet fails closed with REMOTE_NOT_FETCHED until bay status has fetched the live refs. A fetch from another remote URL does not count. The result is blocked, exits 10, and lists no machines, actors or sessions.

CodeMeaning
REMOTE_NOT_FETCHEDThe local cache has no recorded fetch of the configured remote's live refs, so it is not the fleet.

A cursor that bay did not return is a usage error. When this repository lacks the cursor's trunk, bay watch fails. Run it again without --since.

Not built yet

bay has the map, bay watch and the notify adapter. It does not have these parts yet:

  • Talks in the map. talks is always empty, and actors come only from live summaries.
  • Talks in bay watch.
  • A byte budget for the one-line notices an agent hook gives: 2,048 bytes per session per hour.
  • Waking an idle session when another change supersedes its change, or its inputs move.

Limits

  • Machines: the map reads at most 256 summaries, and a host holds at most 256 live refs.
  • Summary: live.json is at most 32,768 bytes and keeps the newest 64 rows.
  • Push rate: one summary push per heartbeat, 10 seconds by default.
  • Freshness: a row drops out after one takeover window, 120 seconds by default.
  • Watch: one watch lists at most 500 landings and 500 events. more says when more follow.
  • Notice: the adapter has 5 seconds per event.
← PreviousAgents and hooksNext →Talk to other actors

On this page

  • When to use the fleet map
  • Show the fleet map
  • Where the map comes from
  • What the map shows
  • Untracked and unknown
  • Ages and clocks
  • Watch for changes
  • What watch lists
  • Get notices
  • What the map cannot do
  • Refusals
  • Not built yet
  • Limits
how it worksfleetroadmapblog© 2026 bay contributors · MIT
Built by Gudjon Gudjonsson, founder and CEO of OZ →Sponsored by OZ