How sessions find a runner
Runners, instances, labels and requirements; how work is spread across instances; why work queues; and why a paused session goes back to the machine it ran on.
Blaise separates what runs from where it runs.
| Concept | What it is | Lives until |
|---|---|---|
| Agent | A preset: configuration, session policy and label requirements | Archived |
| Runner | One authorization of a machine (or fleet) to take sessions for a workspace | Revoked |
| Instance | One connected blaise-agent listen process of a runner, with its labels | Its connection closes |
An agent does not name a runner, and a runner does not name an agent. They are coupled only through labels.
Matching
An instance's labels are a map from a key to a list of values. An agent's requirements have the same shape. An instance satisfies an agent when, for every required key, it declares every required value. Nothing else is evaluated: there are no operators beyond "includes", and no ordering between requirements.
Blaise applies the rule twice. The offer stream only lists work an instance may take, and the claim checks it again, so a modified client cannot take work it was not offered. Labels select capacity; they are not an access control between the runners of one workspace.
Queued work waits
When a session needs a run, Blaise creates an execution in the queued state. It is offered to the matching instances as described in Sharing work across instances, and one of them claims it. Only then does it become starting, and only then does the agent's startup grace period begin.
This means that queued work has no timeout. If every runner is offline, or none matches, the session shows Waiting for a runner and starts as soon as capacity appears. A user can cancel a queued run; it becomes cancelled, which is final and is not a failure.
Sharing work across instances
When several instances can take a run, Blaise prefers the one with the most free capacity.
- Capacity. An instance may declare
capacityinblaise.yml: the most runs it holds at once. An instance without one has no limit. - Load. An instance's load is the number of runs it has claimed that are starting or running. Blaise reads it from its own records, so it stays correct when a process dies.
- Ranking. Matching instances are ranked by capacity minus load. An instance with no declared capacity is ranked as if it had a small fixed capacity, so an idle one is preferred over a busy one. The ranking never limits it. Ties are spread evenly and stay stable as instances come and go.
- First choice, then everyone. The top-ranked instance is offered the run first. If it is still queued after 15 seconds, for example because that instance is stuck, it is offered to every matching instance below their capacity, so a dead instance delays work by at most that long.
- Capacity is enforced at the claim. An instance at its capacity is not offered new runs and cannot claim them, even with a modified client. Cleanup runs are never held back by capacity.
Capacity is declared by the instance and is advisory between instances: it spreads work, it is not an access control. If every matching instance is at capacity, the session shows Waiting for a runner and starts when one finishes a run. The dashboard shows each instance's load against its capacity.
Paused sessions go back to where they ran
A paused session keeps its environment: a worktree, a container, a clone. That environment is on the machine that ran the session, so later runs of the same session are offered only to instances of the runner that ran it before. Fresh sessions have no such preference.
- If that runner is only offline, the run waits for it.
- If that runner was revoked, the run falls back to any matching runner and starts a fresh environment.
Instances that share one authorization
Several instances may use one runner token. Blaise treats them as one pool, so a resume can be
claimed by any of them. Plan for that: either they should see the same file system (a shared volume,
or an environment on a remote host that all of them reach), or the plugs should be
stateless so each instance can rebuild the environment.
If your instances do not share anything, give each its own authorization by running login on each
machine.
Agents that pause, and runners that cannot resume
An agent whose idle policy is pause needs a runner whose plug chain can resume. At startup,
listen works out whether its chain can, and tells Blaise when it connects. An instance that cannot
resume is not offered sessions of pausing agents. If only such instances are online, those sessions
wait; the queued notice says so. Make the plugs resumable
(Make a plug resumable) or
change the agent's idle policy.
Why this design
- Registration is self-service. A runner appears when its CLI registers; there is no per-agent setup step to forget.
- Capacity is visible. The dashboard counts registered runners and instances online, like a list of authorized devices.
- Work is not lost. The database is the source of truth, and a queued execution is offered again on every reconnect.
See also Choose which runners an agent runs on.