Skip to content
WIP

This guide is a work in progress. Barry is also a work in progress.

Distributed nodes

When one machine is not enough, Barry spreads background work across others. This page explains the protocol, placement, and trust model behind that, for operators and contributors. To link a worker step by step, follow Schedule and distribute work, and for every subcommand, read the barry workers reference.

Every workflow run has one owner machine: the installation whose workflow store holds the run’s state and history, and which decides where each activity runs. Every other machine acts as a worker that executes activities for it. The owner machine also works for itself, so an activity that can run locally never leaves it. A worker can serve more than one owner machine, and each owner keeps its own identities and history.

Keep the owner machine distinct from the owner that barry owner shows or binds. That command concerns the user actor who owns an installation; this page always means the machine that holds a run.

Exactly one wire route connects them. The worker calls POST /api/worker on the owner machine over HTTPS, carrying its credential as a bearer token. The owner derives the worker’s identity from that credential, never from the request body, so a worker cannot claim to be another. Over that route a worker says hello, advertises what it can run, claims work while it has free capacity, heartbeats, declines, and reports results. The worker always initiates: inviting a worker never gives the owner an outgoing connection to it.

Remote executors follow the same start, inspect, and stop contract as local ones, through authenticated, capability-limited operations on that route. A worker credential never becomes an admission credential or a general remote shell.

Linking is one-way. barry workers invite on the owner prints a join token once, and barry workers join on the worker redeems it. The owner stores only a SHA-256 hash of the worker’s barry_wk_ credential, beside the worker ID it assigned, and compares hashes in constant time; the worker stores the owner’s address and the credential. The owner can verify a worker without being able to reproduce its secret. The credential opens POST /api/worker and nothing else: every other route answers 401, and the owner never sends a worker its operator secret, actor credentials, vault contents, or an identity’s environment.

You manage that trust without disrupting work:

  • Scoped invites. An invite can name the versioned jobs the worker may claim. The owner enforces the scope at placement, claim, heartbeat, and completion, and a worker cannot widen it by editing its token or advertisement.
  • Invite expiry. An unused scoped invite expires after 24 hours on the owner’s clock. An unscoped invite expires only when you give it an explicit expiry, so revoke unused ones.
  • Rotation. barry workers rotate issues a new credential and keeps the old one working for five minutes, so a running worker keeps going.
  • Revocation. barry workers revoke refuses the worker’s next call and stops its running activities. The owner keeps that work’s physical capacity reserved until it observes the execution stopped.

A copied token acts as the same worker: the next successful hello takes over the session, and fencing locks the displaced process out, as Durable workflows explains. The owner keeps audit rows for invites, joins, rotations, revocations, and takeovers.

A worker advertises its capacity, its labels, its executor capabilities, and the artifacts it holds. Capacity defaults to four concurrent activities and tops out at 100, set by BARRY_WORKFLOW_CAPACITY. Labels steer work to the machine that fits it. Every worker advertises its operating system and architecture, and you add more with BARRY_WORKFLOW_LABELS, a comma-separated KEY=VALUE list such as model=local,gpu=metal. Set both in the worker’s barry.yaml under services.workflows.runtime.env, then restart the service with barry service restart workflows.runtime. Claim only labels the machine can satisfy: a label does not install model software, and a configured label is not a health check.

Advertisements expire after 15 seconds and refresh within five, so the owner stops offering work to a worker that goes quiet. barry workers status lists what a machine offers and why it withholds anything, and Background work shows each worker by a human name as Ready, Busy, Offline, or Needs setup.

The owner places each activity in layers, and a later layer never overrides an earlier one:

  1. Hard eligibility. The run’s authority, the job’s own node bounds, the grant’s node bounds, the worker’s scope, verified code and resources, and the required executor capability. Every placement records why it excluded each candidate.
  2. Capacity. Three separate resource layers must all have room: the logical quota that admission reserved, physical slots on the execution host, and shared capacity on the inference endpoint.
  3. Preference. Locality, an already warm model, and your stated preference break ties among eligible hosts.

The logical layer keeps the compatibility limits for detached agents: four per caller, 16 unscheduled, and a protected four for scheduled work. A long wait for a timer or a decision can hold logical quota while it holds no physical slot. Physical slots stay reserved until the execution host confirms cessation. Endpoint capacity spans owners: an endpoint-owning broker or a worker-local arbiter enforces one aggregate cap, so two owners cannot each spend the same endpoint’s allowance.

Where an agent runs and where its model runs count as separate placements. A run records its execution host and its inference endpoint as separate fields, along with the requested and resolved model, the selection provenance, and limits. Barry never swaps models silently after a failure; a fallback has to exist as an authored step or an authorized attempt policy.

Not every task suits every node, and remote execution never silently weakens a task’s contract. A task runs on a worker only when that worker can enforce everything the task requires: its credentials, its workspace, its runtime, and its bounds. Otherwise placement reports a clear eligibility result and the task stays on the owner machine, or stays visibly blocked.

Bag jobs follow fixed rules. A job can run remotely when it carries a version, addressed as bag:name@1, uses the Node runtime, declares no secrets, and touches no files. Owner and worker pin it by digest and must have the same bag version installed. Eligible scripts and agent actions also run remotely under the same journal, receipt, and identity rules as local runs, using credentials provisioned for that worker or a narrowly scoped delegated credential. A remote run declares its workspace: a verified pre-provisioned one, an isolated input bundle, or none. Remote changes come back as artifacts or a patch for an authorized publication step, never as a silent overwrite of a shared repository.

A worker you invite holds a credential, not proof of who operates the machine. Scopes narrow what a credential can claim, but they do not verify results or sandbox a malicious operator: a stolen credential can read the inputs of jobs in its scope and report fabricated results. Invite only machines you operate, and publish the owner’s workflow service on a private HTTPS route, because a public route also exposes its console sign-in page.

Linking a worker distributes background work. It does not share an interactive session, copy your stores, or move a running session between machines, and it does not make a worker’s output trustworthy on its own. Treat workers as more hands for work whose definition you already trust. The distributed trust notes and the two-machine walkthrough cover the details.