Skip to content

How to launch a formation of capsules

A formation is a group of capsules on one machine that work on one task together: one member receives the task and hands parts of it to the others. This guide scaffolds a two-member formation, lead and worker, launches it on a task that lead hands to worker, and follows the hand-off through the trace until the formation ends.

The relevant manifest options are:

Option Controls
exports.peer_tasks.accept Whether the member serves tasks other members send it
network.authentication Whether the member's door refuses callers without a token
capabilities.network.allow Where the member may connect, other members' doors included
lifecycle.task_acceptance How many tasks the member takes
lifecycle.after_task Whether the member exits or waits after a task
inference.system_prompt What each member is told it is for

The output below is from one real run with OpenAI as the provider and HOME set to a scratch directory, /tmp/crew-run-d078/home. Your ids, ports, pids, timings and paths will differ.


Step 1 — scaffold the formation

mur new --roster names the provider's driver, endpoint and model from ~/.murmur/config.yaml. With no inference: block it uses Anthropic. To use OpenAI, set the provider before you scaffold:

Nothing to set.

mur config set -g inference.provider openai
Set inference.provider in ~/.murmur/config.yaml

In an empty directory, scaffold a formation named crew:

mur new --roster crew
Scaffolded formation 'crew' in ./crew
  crew/roster.yaml          lead is the entry member; lead may call worker
  crew/lead/murmur.yaml     capsule local/crew-lead@0.1.0
  crew/worker/murmur.yaml   capsule local/crew-worker@0.1.0, serves peers
  Driver: murmur/driver-openai@0.19.0, the newest that fits this mur in github:murmur-nexus/default-artifacts <release>

Next:
  mur config set -g credentials.OPENAI_API_KEY <your key>
  mur install -g murmur/driver-openai@0.19.0
  mur build crew/lead && mur install -g crew/lead/crew-lead-0.1.0.mur.zip
  mur build crew/worker && mur install -g crew/worker/crew-worker-0.1.0.mur.zip
  mur run --roster crew --task "<your task>"

The scaffold writes three files and nothing else. Every key in them carries a comment and a link to its reference entry. The Next: lines are the rest of this guide, in order. See mur new --roster.


Step 2 — read what the generated files declare

crew/roster.yaml

The roster names the members, marks one as the entry member, and says who may call whom:

members:
  - name: lead                      # its name in this roster, used by reachability
    capsule: local/crew-lead        # built from lead/murmur.yaml
    version: 0.1.0                  # the exact version installed
…
    entry: true
  - name: worker                    # its name in this roster, used by reachability
    capsule: local/crew-worker      # built from worker/murmur.yaml
    version: 0.1.0                  # the exact version installed
…
reachability:
  - from: lead                      # the calling member
    to: [worker]                    # the members lead may call
  • The entry member. lead has entry: true, so it receives the formation's task, and its outcome is the formation's outcome. No rule may list it in to: the entry member is never called. See The entry member.
  • The edge. The one rule lets lead call worker. That edge is what gives lead the call-member tool, and what makes the launcher issue lead a credential for worker's door. worker may call nobody. See reachability.

crew/worker/murmur.yaml

worker consents to tasks from other members, and waits at its door between them:

lifecycle:
  task_acceptance: queue            # take tasks one after another
  after_task: sleep                 # wait at the door for the next one
…
exports:
  peer_tasks:                       # tasks another member sends to this door
    accept: true                    # serve them; absent or false refuses them
…
network:
  authentication:                   # refuse every caller without a token this session minted
    scheme: bearer                  # Authorization: Bearer <token>, the only scheme
  • Consent. A member a rule calls must declare exports.peer_tasks.accept: true, or admission refuses the roster with E-ROS-005.
  • Authentication. A roster with any edge requires every member's door to require a token, or admission refuses it with E-ROS-006. The door then lets in only the credential the launcher issued for it. See Authentication.
  • Lifecycle. queue and sleep keep worker serving for as long as the formation runs.

crew/lead/murmur.yaml

lead is told to hand its task on, declares no lifecycle, and may reach other members' doors:

  system_prompt: "You are 'lead', the entry member of the formation 'crew'. You do not do the task yourself: worker does it. Your first reply is a call-member tool call that hands worker the task you are given, stated in full. Then end your turn while worker works. When worker's answer arrives in this conversation, answer with it. If the runtime says worker gave no answer, say so in your reply and do not answer the task yourself."

# How the capsule takes tasks is not declared here: mur run --roster sets the entry member's
# lifecycle on every launch. With --task it runs that one task and exits, which ends the
# formation; without, it takes tasks at its door one after another until the formation is
# stopped.
# https://docs.murmur.nexus/reference/cli/#mur-run-roster
…
capabilities:
  network:                          # IP destinations the capsule may reach
    allow:                          # anything not listed is denied
      - localhost                   # every member's door, at any port and scheme
…
network:
  authentication:                   # refuse every caller without a token this session minted
    scheme: bearer                  # Authorization: Bearer <token>, the only scheme
  • System prompt. lead does not do the task itself: it hands the task to worker with call-member, ends its turn, and answers with what comes back.
  • Lifecycle. lead declares none: mur run --roster sets the entry member's lifecycle on every launch. With --task, lead takes that one task and exits when it ends, and its exit ends the formation. Without --task, it takes tasks at its door until the formation is stopped.
  • Egress. The roster grants lead a name and a credential, not network access. A member's door is served on loopback at a port chosen at launch, so lead allows the bare host localhost. Without it every call fails, and the launch prints W-RUN-008. See Giving a member work.
  • Network authentication. lead declares it too: with any edge in the roster, every member must, whether or not it serves peers.

Step 3 — store the key and install the members

Store the provider key in the global config. The key is the one value only you can supply, and mur run --roster fails without it:

mur config set -g credentials.ANTHROPIC_API_KEY <your key>
mur config set -g credentials.OPENAI_API_KEY <your key>
Set credentials.OPENAI_API_KEY in ~/.murmur/config.yaml

Each member reads the key from credentials: at launch, and neither prints W-SEC-027.

Install the driver both members declare:

mur install -g murmur/driver-anthropic@0.19.0
mur install -g murmur/driver-openai@0.19.0
Installed murmur/driver-openai@0.19.0 from github:murmur-nexus/default-artifacts
Different ways to install artifacts

mur install fetches an artifact from the source of its scope. murmur/ artifacts come from the built-in source, murmur-nexus/default-artifacts, with no configuration.

Option A — name the source of a scope in ~/.murmur/config.yaml:

registry:
  sources:
    - repo: <owner>/<repo>
      token: "${GITHUB_TOKEN}"

Then install by full name, with or without a version:

mur install <owner>/<artifact-name>@<version>

Option B — pass a full GitHub reference and skip configuration entirely:

mur install github:<owner>/<repo>@<tag>

See Installing artifacts to learn more.

Build each member and install it into the global store, where mur run --roster looks for it:

mur build crew/lead && mur install -g crew/lead/crew-lead-0.1.0.mur.zip
mur build crew/worker && mur install -g crew/worker/crew-worker-0.1.0.mur.zip
Built artifact: crew/lead/crew-lead-0.1.0.mur.zip
Installed local/crew-lead@0.1.0 from crew/lead/crew-lead-0.1.0.mur.zip
Built artifact: crew/worker/crew-worker-0.1.0.mur.zip
Installed local/crew-worker@0.1.0 from crew/worker/crew-worker-0.1.0.mur.zip

Optionally, check the formation from the crew directory. With no murmur.yaml there, mur doctor checks the roster against the installed members:

cd crew
mur doctor
cd ..
No murmur.yaml in /tmp/crew-run-d078/work/crew: checking its roster.yaml. Run mur doctor in a member's source directory to check that member's artifacts.

Roster
  file: /tmp/crew-run-d078/work/crew/roster.yaml
  lead     local/crew-lead@0.1.0     entry   refuses peers   authenticated door
  worker   local/crew-worker@0.1.0           serves peers    authenticated door
  reachability: lead → worker

All checks passed.

Step 4 — launch the formation

Launch the formation with the last Next: line, your task in place of <your task>:

mur run --roster crew --task "Write a four-line poem about a lighthouse keeper."
…
formation: frm_01a1100295f47ea3b227ddf4612759f3
  peer   worker  local/crew-worker@0.1.0  pid 2768649  ses_01a11002968476428a785ab0b3ae4fbb  http://localhost:39151
                 workdir /tmp/crew-run-d078/home/.murmur/formations/frm_01a1100295f47ea3b227ddf4612759f3/worker
  entry  lead    starting
…
murmur: url localhost:42759
murmur: auth bearer (mur token ses_01a11002984c7bd0ad3a7b1927d94d5d)
session: ses_01a11002984c7bd0ad3a7b1927d94d5d
status:  ok
[worker] [capsule-runtime] formation lifeline closed — the formation has ended; cancelling live tasks and ending the session

The task is the plain task. lead's system prompt already tells it to hand the task to worker.


Step 5 — read the readiness lines

The launcher starts worker first, waits for its door, and only then starts lead:

Line Means
formation: frm_… The formation id minted for this launch. Every member carries it, and its trace is addressed by it
peer worker … worker is ready: its process id, its session id, and the door that answered as that session
workdir … worker's work folder, under ~/.murmur/formations/<frm_id>/worker. Its sessions' records are under the same path mirrored in ~/.murmur/records
entry lead starting Every peer is ready, and lead is starting with the task
murmur: url, murmur: auth, session: lead's own startup lines: its door, that the door requires a token, and its session id. lead works in crew, and its sessions' records are under ~/.murmur/records/<crew's path>/
status: ok lead's task ended ok
[worker] … formation lifeline closed … worker saw the formation end and wound down

No member prints a door token. While lead runs, mur token <lead's session id> prints the operator token for its door — see mur token. A peer that does not come up within 180 seconds refuses the whole launch, and lead never starts. See Readiness and Member directories.


Step 6 — follow the hand-off from lead to worker

lead's model calls call-member once, with worker and the task text. The call returns as soon as worker's door holds the task, or at once when worker is busy, in which case the runtime keeps offering it the task. lead's model then ends its turn. worker runs the task in its own directory and answers. lead's same task then continues with one new message holding worker's answer, fenced as coming from member:worker, and lead answers with it.

From here on, work in the crew directory. Name the formation by its id. mur trace show searches lead's sessions and every member's directory under ~/.murmur/formations/:

cd crew
mur trace show frm_01a1100295f47ea3b227ddf4612759f3
Different ways to identify a session

mur trace show with no argument reads the most recent session:

mur trace show

To name another one, pass an ordinal counting back from the newest (@2), the last 4 or more characters of its ID (3e4b), the full ID, or a path to its trace.jsonl:

mur trace show @2
mur trace show 3e4b
mur trace show ses_6801f81dd28b4a9daf434e8324c4793e
mur trace show path/to/trace.jsonl

Use --workdir <path> if your session directories are not under ./workdir. Every command that names a session takes the same addresses — see Session addresses.

Other trace exploration commands

mur trace has four subcommands for exploring session output:

mur trace show — print the full trace for a session to the terminal.

mur trace steps — show a turn-by-turn summary of what the agent did in a session. Pass --verbose to include a truncated summary of each tool's input.

mur trace diff — compare the traces of two sessions side by side, or with no arguments the two most recent. Useful for spotting behavioural regressions between runs.

mur trace report — generate a structured summary report from a session's trace. Covers token usage, tool calls, latency, and other session-level metrics.

── Formation ────────────────────────────────────
formation:  frm_01a1100295f47ea3b227ddf4612759f3
searched:   /tmp/crew-run-d078/home/.murmur/records/tmp/crew-run-d078/work/crew/workdir
searched:   /tmp/crew-run-d078/home/.murmur/records/tmp/crew-run-d078/work/crew
searched:   /tmp/crew-run-d078/home/.murmur/records/tmp/crew-run-d078/home/.murmur/formations/frm_01a1100295f47ea3b227ddf4612759f3/worker
ses_01a11002968476428a785ab0b3ae4fbb  worker  crew-worker@0.1.0  ok
ses_01a11002984c7bd0ad3a7b1927d94d5d  lead    crew-lead@0.1.0    ok  may call worker
calls:      1
mcl_01a11002a5d57021b9017e9e2e31a465  lead → worker  completed  2.0s  delivered
Line Means
searched: Each session root searched: the roster directory's, then each peer's own
worker … worker's session: its roster name, its capsule, and how it ended
lead … may call worker lead's session, and the member the roster lets it call
calls: 1 The formation made one call-member call
mcl_… lead → worker … The call: its id, who called whom, completed, how long worker took to answer, and delivered because the answer reached lead's task

In a larger formation each call made inside a handed-over task is nested under the call that handed it over, and a call one side of which was not found carries a note saying which. See Listing a formation.

The call row comes from three records. In lead's trace, trace.jsonl in its records directory, the call and its outcome:

{"event_type":"member_call_start","event_id":"evt_01a11002a5e27232b8c9a161b0aa15e5","parent_id":"evt_01a1100298c178d1a950c189b9b5a2c0","session_id":"ses_01a11002984c7bd0ad3a7b1927d94d5d","timestamp":1791269971426,"task_id":"tsk_01a1100298c57060b6d732feee045060","call_id":"mcl_01a11002a5d57021b9017e9e2e31a465","member":"worker","member_task_id":"tsk_01a11002a5db7fe0a98b4e4e743aa444"}
{"event_type":"member_call","event_id":"evt_01a11002adcb7e2394fcf3c1d0e04cd9","parent_id":"evt_01a1100298c178d1a950c189b9b5a2c0","session_id":"ses_01a11002984c7bd0ad3a7b1927d94d5d","timestamp":1791269973451,"task_id":"tsk_01a1100298c57060b6d732feee045060","call_id":"mcl_01a11002a5d57021b9017e9e2e31a465","member":"worker","member_task_id":"tsk_01a11002a5db7fe0a98b4e4e743aa444","status":"completed","duration_ms":2037,"output":"He tends the lamp while black waves climb,  \nA steady star against the night,  \nThrough salt and storm, he keeps his time,  \nGuiding lost sails toward the light.","truncated":false,"delivered":true}
Field Here
call_id mcl_…, the same on both records
member_task_id worker's task id for this call
status completed: worker finished the task. A call that brought no answer reads failed, canceled, rejected, timed_out, unreachable, abandoned or no_answer — see member_call
duration_ms From the call to worker's answer
output worker's answer, as lead's model received it
delivered true: the answer reached lead's task

In worker's trace, the task arrives named for the member that sent it:

{"event_type":"a2a_task_received","event_id":"evt_01a11002a5dc79e2bd4a5bf91768acba","parent_id":"evt_01a11002969c75d28c8244f84ad1425f","session_id":"ses_01a11002968476428a785ab0b3ae4fbb","timestamp":1791269971420,"task_id":"tsk_01a11002a5db7fe0a98b4e4e743aa444","context_id":"ctx_01a11002a5db7fe0a98b4e57a39f2e8a","message_id":"msg_mcl_01a11002a5d57021b9017e9e2e31a465","traceparent_from_caller":null,"caller":"member","caller_member":"lead","message_sha256":"5c1f0e9b7a3d42c8e6f1a0b9d8c7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d9"}

mur trace show matches this record to the call by its task_id, the call's member_task_id. caller is member, caller_member is lead, and the message id carries the call id. message_sha256 is the hash of the task's message text; under trace.capture: content the text is stored behind it. See call-member and the member_call record.

lead's answer, and so the formation's, is in out/result.txt in its session's records directory. lead works in the project directory, so that is ~/.murmur/records/<project directory>/<session_id>/:

cat ~/.murmur/records$PWD/ses_01a11002984c7bd0ad3a7b1927d94d5d/out/result.txt
He tends the lamp while black waves climb,  
A steady star against the night,  
Through salt and storm, he keeps his time,  
Guiding lost sails toward the light.

Step 7 — read each member's own trace

Show lead's session from its root, crew itself:

mur trace show ses_01a11002984c7bd0ad3a7b1927d94d5d --workdir .
── Session ──────────────────────────────────────
session:    ses_01a11002984c7bd0ad3a7b1927d94d5d
formation:  frm_01a1100295f47ea3b227ddf4612759f3
capsule:    crew-lead v0.1.0
model:      gpt-5.6-luna
status:     ok
duration:   7.1s
capabilities: network
tools:      call-member
…
── Tool calls ───────────────────────────────────
count:      1  (1 ok, 0 error)  success 100.0%
latency:    avg 13ms
  turn 0  call-member 13ms ✓  {"member":"worker","task":"Write a four-line poem about a lighthouse keeper. Return only the four-line poem, with exactl…
  turn 1  end_turn
…
── Member calls ─────────────────────────────────
mcl_01a11002a5d57021b9017e9e2e31a465  worker  tsk_01a11002a5db7fe0a98b4e4e743aa444  completed in 2.0s

── Formation ────────────────────────────────────
formation:  frm_01a1100295f47ea3b227ddf4612759f3
searched:   /tmp/crew-run-d078/home/.murmur/records/tmp/crew-run-d078/work/crew
searched:   /tmp/crew-run-d078/home/.murmur/records/tmp/crew-run-d078/home/.murmur/formations/frm_01a1100295f47ea3b227ddf4612759f3/worker
ses_01a11002968476428a785ab0b3ae4fbb  worker  crew-worker@0.1.0  ok
ses_01a11002984c7bd0ad3a7b1927d94d5d  lead    crew-lead@0.1.0    ok  may call worker
calls:      1
mcl_01a11002a5d57021b9017e9e2e31a465  lead → worker  completed  2.0s  delivered

tools: call-member is there because the roster lets lead call worker. The Member calls section lists each call this session made with the member, its task id, and how it ended. The Formation section is the listing from Step 6.

Show worker's session from its own directory:

mur trace show ses_01a11002968476428a785ab0b3ae4fbb --workdir ~/.murmur/formations/frm_01a1100295f47ea3b227ddf4612759f3/worker
── Session ──────────────────────────────────────
session:    ses_01a11002968476428a785ab0b3ae4fbb
formation:  frm_01a1100295f47ea3b227ddf4612759f3
capsule:    crew-worker v0.1.0
model:      gpt-5.6-luna
status:     ok
duration:   7.7s
…
── Formation ended ──────────────────────────────
formation_ended  frm_01a1100295f47ea3b227ddf4612759f3  the formation ended; this session wound down

── A2A ──────────────────────────────────────────
received:   1 task — member 1
sent:       0 messages

── Formation ────────────────────────────────────
formation:  frm_01a1100295f47ea3b227ddf4612759f3
searched:   /tmp/crew-run-d078/home/.murmur/records/tmp/crew-run-d078/home/.murmur/formations/frm_01a1100295f47ea3b227ddf4612759f3/worker
ses_01a11002968476428a785ab0b3ae4fbb  worker  crew-worker@0.1.0  ok
calls:      1
mcl_01a11002a5d57021b9017e9e2e31a465  lead → worker  unknown  -    lead's trace not found; worker's task ended ok

worker has no tools: line: the roster lets it call nobody, so it has no call-member. Its Formation section searches its own root and the peers' roots, not the crew directory where lead records, so the call reads unknown with lead's trace not found. The formation id, run from crew as in Step 6, finds both sides.


Step 8 — see how the formation ends

Launched with --task, lead takes that one task and exits when it ends. When lead's process ends, the launcher closes every other member's lifeline, and the formation is over:

  1. worker appends formation_ended to its trace, before anything else its wind-down writes:

    {"event_type":"formation_ended","event_id":"evt_01a11002b48f78b1ad023e951090a20d","parent_id":"evt_01a11002969c75d28c8244f84ad1425f","session_id":"ses_01a11002968476428a785ab0b3ae4fbb","timestamp":1791269975183,"formation_id":"frm_01a1100295f47ea3b227ddf4612759f3"}
    
  2. worker cancels anything still in flight, ends its session and exits 0. Its session_end says ok when it was idle, as here, and formation_ended when the formation's end canceled a task it was running; neither prints an error.

  3. The launcher exits with lead's exit status: 0 here, because lead's task ended ok.

Nothing of the formation is left running:

mur ps
no running capsules

Launched with --task, the formation lives for that one task. To run it again, launch it again with a new task: the new launch is a new formation with a new id.

Launched without --task, the formation stays up and lead takes tasks at its door one after another, from the operator or from a client such as nexus. mur ps --json names lead as the formation's entry_member, mur token <lead's session> prints the token its door takes, and mur stop frm_<id> ends the formation; the launcher then exits 143. See Launching a formation.

mur run --roster refuses --resume, and a member's session resumed by hand belongs to no formation. See How a formation ends and Resuming a member's session.


Summary

Feature / setting How it works
Scaffold mur new --roster crew writes roster.yaml and one murmur.yaml per member, with the provider from ~/.murmur/config.yaml, and prints the Next: steps
Entry member entry: true in roster.yaml. It receives the formation's tasks and is never called by a peer. With --task the formation ends when that task ends; without, it takes tasks at its door until mur stop
Edge A reachability rule. It gives the caller call-member and a credential for the callee's door
Consent exports.peer_tasks.accept: true on every member a rule calls
Authentication network.authentication on every member once the roster has any edge
Egress The caller's capabilities.network.allow lists localhost
Key mur config set -g credentials.<KEY_VAR> <your key>, first of the Next: steps
Launch mur run --roster crew --task "…" starts the peers, waits for each door, then starts the entry member
Hand-off call-member returns at once: started when the callee holds the task, or busy when it has no room, and the runtime keeps offering a busy callee the task. The answer, or word that none came, comes back into the caller's same task. A member with nothing to give ends its task with end-without-answer, and its caller's call ends no_answer
Trace mur trace show frm_<id> from the roster's directory lists every member by roster name, every call — who called whom, how it ended, whether the answer was delivered, and how many offers a busy callee turned away — and every task a person sent a member over its door, such as one nexus sent with the operator token. Behind each call row, member_call in the caller's trace and a2a_task_received with caller: "member" in the callee's; behind each task row, the member's a2a_task_received with its caller
End With --task, the entry member exits after its task; every other member records formation_ended and exits; the launcher exits with the entry member's status. Without, mur stop frm_<id> ends every member and the launcher exits 143