Menu Nodes and orchestration

indra CLI

Nodes and orchestration

Run agents on this machine and over SSH, and chain them into workflows.

Agents can run on this machine and on any machine you can SSH into. A node is one such machine; local is always there.

[local]
max_parallel = 4                         # agents at once on this machine

[[node]]
name         = "build-box"
ssh          = "ci@build-box.internal"   # host, user@host, or a Host alias from ~/.ssh/config
max_parallel = 2
targets      = ["/srv/api", "/srv/web"]  # codebases on that machine

Each remote node needs indra on its PATH (or set indra = "/path/to/indra"), and ssh <destination> must work without a password prompt (keys or an agent). Then:

$ indra nodes
local          ok    laptop     (macos aarch64, 10 cpus, indra 0.1.0, max 4 agents)  runners: claude
build-box      ok    ci-7       (linux x86_64, 16 cpus, indra 0.1.0, max 2 agents)   runners: claude, codex
gpu-box        DOWN  me@gpu-box  ssh: Could not resolve hostname gpu-box: Name or service not known

In the console, Nodes shows each machine, its runners, its capacity and the review runs on its disk (over SSH, or over the link for a machine that dials in). Click a remote run to copy its text files to the hub (<data dir>/fetched/<node>/<run>) and open it like a local one: findings, triage, comparison, memory, publish and post work on the copy, and changes stay in the copy. Launch can start an agent on any node. A remote agent streams into the same live view as a local one, with its subagents and tool calls, and Stop stops it on the remote.

Machines that dial in (no SSH)

A node can connect to the hub instead of being reached over SSH. On the Nodes page, Add a machine gives a one-time command; run it on that machine (with indra installed):

indra node connect https://hub.example:7878 --name build-box --code ABCDE-FGHJK --target /srv/api
# later starts: indra node connect https://hub.example:7878 --name build-box --target /srv/api

The node runs only runners from its own configuration, in directories that exist on it, exactly as with SSH. Builds without the webrtc feature (cargo install --no-default-features) relay everything over the WebSocket.

Workflows

A workflow is a graph of agent tasks, each on a node, run in dependency order:

[[workflow]]
name = "nightly"
description = "Review the services on the build box and the web app here, then summarise."

[[workflow.task]]
id      = "api"
node    = "build-box"
target  = "/srv/api"
prompt  = "/indra full security review of {target}"
retries = 1                              # one more attempt if it fails

[[workflow.task]]
id     = "web"
node   = "local"                         # the default
target = "../web"                        # local targets are relative to indra.toml
prompt = "/indra full security review of {target}"

[[workflow.task]]
id     = "summary"
target = "."
prompt = "Summarise tonight's reviews and list new critical findings."
needs  = ["api", "web"]                  # runs only if both succeed

Run one from the terminal (cron, CI) or from the console's Workflows page:

$ indra run nightly
17:01:29 workflow 'nightly' started
17:01:29 api                build-box    running  (process #1, attempt 1)
17:01:29 web                local        running  (process #2, attempt 1)
17:14:52 web                local        succeeded
17:21:07 api                build-box    succeeded
17:21:07 summary            local        running  (process #3, attempt 1)
17:23:40 summary            local        succeeded

17:23:40 workflow 'nightly' succeeded

Inputs. A workflow can ask for values when it runs, and its tasks use them as {input.NAME} in their prompt, target, profile or model:

[[workflow]]
name = "triage-pr"

[[workflow.input]]
name     = "pr"
label    = "Pull request"
required = true
pattern  = '[0-9]{1,9}'                  # text inputs may require a form
[[workflow.input]]
name = "target"
kind = "target"                          # one of the harness's targets
[[workflow.input]]
name    = "depth"
kind    = "choice"
options = [{ value = "", label = "auto" }, { value = "--tier=2", label = "standard" }]
[[workflow.input]]
name      = "dry_run"
kind      = "bool"
default   = "true"
when_true = "--dry-run"                  # what {input.dry_run} becomes when ticked
[[workflow.input]]
name = "profile"
kind = "profile"                         # a configured profile, or none

[[workflow.task]]
id      = "review"
target  = "{input.target}"
profile = "{input.profile}"
prompt  = "/sh-pr-review {input.pr} {input.depth} {input.dry_run}"

Kinds are text (the default), choice, bool, target and profile. Each value is checked when the workflow starts: required ones present, text matching its pattern and on one line, choices and targets among their options, profiles configured. An empty input leaves no gap in the prompt.

Built in: with the indra harness, two workflows are always there (a configured workflow of the same name replaces one): pr-review (pull request, checkout, profile, depth, fail-on, deep, dry run; dry run is on by default) and security-review (codebase, optional focus, profile). Run any workflow from Runs > Run a workflow or the Workflows page (both ask for its inputs), or from the terminal:

indra run pr-review --input pr=https://github.com/acme/shop/pull/7 --input target=$PWD --input depth=--tier=2
indra run                                # lists the workflows and their inputs (* required)

The graph editor. Workflows > New workflow (or Edit graph on a workflow) opens a canvas: drag tasks, skills, saved tasks and other workflows in from the palette (or select one and press Enter), drag from a step's right edge to another step to make it run after the first, and edit the selected step on the right. Remove a step with its Delete step button, the x on the selected step, or the Delete key (Undo brings it back); remove an arrow by selecting it and pressing Delete. Saving writes the same [[workflow]] TOML, with each step's position, so indra run and the text editor keep working.

Skills as steps. A step can run one of the harness's skills instead of a free prompt: drag it from Skills in the palette, or write skill = "<name>". For Claude Code it runs as the skill's slash command, /<skill> <prompt>, so prompt holds the skill's arguments (default {target}); other runners are told to use the skill.

[[workflow.task]]
id     = "recon"
skill  = "sh-security-review"
prompt = "{target} stage:recon"          # becomes: /sh-security-review {target} stage:recon

Saved tasks ([[component]] in the file) are tasks kept for reuse: a task, a skill step or a prompt with its profile, runner, node and target, that any workflow can drag in. A step that starts from one overrides any field, and changing the saved task changes every workflow that uses it. The editor's Save for reuse turns a step into one. (A task is one step of one workflow; a saved task is a template for steps. Where a step runs is its node and target, the repository or folder.)

[[component]]
name    = "review"
prompt  = "/indra full security review of {target}"
profile = "worker"
retries = 1

[[workflow.task]]
id        = "api"
component = "review"
node      = "build-box"
target    = "/srv/api"

Workflows as code. A workflow can live in the repository it works on, as .indra/workflows/<name>.toml, to be reviewed, committed and shared like any other code. Every console and indra run with that repository as a harness root or target picks it up, and a git pull that changes the files is seen without a restart.

# api/.indra/workflows/triage.toml
[[workflow]]
name = "triage"

[[workflow.task]]
id      = "recon"
skill   = "sh-security-review"
prompt  = "{target} stage:recon"
profile = "planner"                      # a built-in profile, so it works for everyone

[[workflow.task]]
id        = "lint"
component = "lint"                       # a saved task kept in the same file
needs     = ["recon"]

[[component]]
name   = "lint"
prompt = "Run the linters on {target} and fix what they report."

Workflows compose. A task with workflow = "<name>" runs that workflow as one step: its first tasks start after the step's needs, and anything that needs the step waits for its last tasks. Its tasks show up as <step>__<task> in the run. A workflow cannot include itself.

indra run exits 0 only if every task succeeded; Ctrl-C cancels the run and stops its agents. indra run without a name lists the configured workflows.

The Workflows page also has fan-out: one prompt across any set of targets on any nodes, in parallel within each node's capacity. That is the quickest way to review ten repositories spread over three machines.

How remote execution stays safe

Remote nodes need a POSIX shell (Linux, macOS, BSD). local works everywhere.