Skip to content

How to run a capsule on your subscription

A capsule normally reaches a model over HTTPS with an API key you declare in its manifest. Under transport: process it instead drives a harness CLI you are already logged into — the claude binary on your machine — so the work is billed to that login rather than to a key. Murmur still owns the task, the tools, the trace and the A2A door; the harness owns the model calls and the conversation.

What is a process driver?

A process driver is a WASM artifact declared with runtime: driver that knows how to drive one harness CLI: which executable to run, which arguments to pass, how to read its output, and what its exit codes mean. Murmur's runtime holds none of that — it resolves a binary, moves bytes, and records what the driver says happened.

A process driver is granted nothing: no environment, no files, no network, and no Murmur host interface. murmur/driver-claude-code is the driver for the Claude CLI.

The relevant manifest options are:

Option Controls
inference.transport Whether the capsule calls a provider API or drives a harness subprocess
inference.driver.artifact The process driver artifact that knows this harness
inference.command The executable to run instead of the one the driver names
inference.max_turns How many model steps one task may take
inference.system_prompt_file The instructions the agent runs under
capabilities.env.allow Every environment variable the harness process may see
lifecycle.conversation Whether tasks in one context continue the same harness session

What this transport costs you

Read this before you choose it. Four things are true here that are not true of transport: http.

Cost What it means
The harness runs under the capsule's class The claude process runs under the same containment as a shell command: on scoped it is in the session's network and PID namespaces behind the egress proxy, under seccomp, Landlock, the session cgroup and rlimits; on sealed it also runs inside the composed root. What it needs beyond that — its install folder, its config and login folder, its inference host — the process driver declares and the manifest grants through capabilities.harness and capabilities.network.allow. See The process harness.
The capsule depends on your machine It runs on your login and on whichever claude version is installed. The manifest pins neither, so the same manifest on two machines is not the same capsule.
Murmur counts what it is told The harness reaches its provider with its own credentials, so no request passes through Murmur. The token counts in the trace are the harness's own report of what it spent, relayed by the driver — not requests Murmur measured. inference.max_session_tokens and spend.machine_tokens_per_day are enforced against those reported numbers.
The harness's own configuration does not load The driver launches the harness with --setting-sources "", so the harness reads none of its own configuration on this machine: its CLAUDE.md, its settings and its own hook scripts stay out. The agent's instructions come from the manifest, which can name files in the work folder, such as the repository's AGENTS.md or CLAUDE.md, through inference.instruction_files. The capsule's own runtime: hook artifacts run exactly as they do on transport: http.

These are the harness's own numbers, not Murmur's. On transport: http Murmur builds every request and counts it before sending; here it counts what the harness said it spent, and a harness that reports nothing leaves the count absent rather than zero. A manifest that sets either ceiling against a driver that reports no usage is refused at launch with E-RUN-038, so a ceiling never stands over a run nothing could measure.


Step 1 — log in to the Claude CLI

The capsule authenticates as you. Install the CLI and log in once:

claude login
claude --version

The version it prints is the version your capsule runs on — the manifest pins neither it nor your login.

The capsule reads that login out of ~/.claude under the HOME it is given, which is why HOME is one of the variables the driver requires. An API key cannot reach the harness through the manifest at all: capabilities.env.allow refuses every credential-shaped name with E-CAP-016, ANTHROPIC_API_KEY among them.

A run whose harness session reports an API key anyway prints warning[W-SEC-031] and keeps going: the harness still reports what it spent and both ceilings still count it, but the bill arrives against that key rather than the subscription. On a subscription login the warning does not appear.

Step 2 — create murmur.yaml

Create a murmur.yaml file. inference.driver.artifact is required under this transport, and every variable the driver's describe() requires must appear in capabilities.env.allow:

name: my-capsule
version: "0.1.0"

artifacts:
  - name: murmur/driver-claude-code
    version: "0.9.0"
    runtime: driver
  - name: murmur/tool-read
    version: "0.4.0"
    runtime: tool
  - name: murmur/tool-write
    version: "0.4.0"
    runtime: tool

capabilities:
  env:
    allow:
      - HOME
      - PATH
  resources:
    memory_bytes: 2147483648   # the RLIMIT_AS the harness runs under; raise it if a turn fails to start

inference:
  transport: process
  max_turns: 100
  system_prompt_file: ./instructions.md
  driver:
    artifact: murmur/driver-claude-code

lifecycle:
  conversation: threaded

The harness runs under the capsule's class. A process driver built against murmur:driver@0.6.0 declares what its harness needs beyond that — Claude Code's install folder, its config and login folder, and its inference host — and the manifest grants the matching directories under capabilities.harness.paths and the host under capabilities.network.allow. mur run refuses a process driver built against an earlier murmur:driver version with E-WIT-LEVEL-001 at every containment class, so install a murmur/driver-claude-code release built against 0.6.0, then run mur run --explain-scope to see each need it declares and grant it. Leave inference.model out to run on whatever model your subscription defaults to.

Create an instructions.md file next to it with the agent's instructions. That file is the whole of what the agent is told about its job.

Step 3 — install the artifacts

mur install
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.

The driver is resolved like any other artifact. mur run then checks it three times before the harness starts, and refuses the launch on the first failure:

Check Refused with
The artifact exports the process driver interface at the version this mur serves E-WIT-LEVEL-001 for another version, E-RUN-029 for none
It loads, and its describe() returns usable variable names E-RUN-032
Every variable describe() requires is declared in capabilities.env.allow E-CAP-019

Step 4 — run a task

Create a task.md file with the work you want done, then run the capsule:

mur run --task task.md
mur run --task task.md
murmur: url localhost:52222
session: ses_019ed2af53da75c2aefee84ee10c34af
status:  ok

The harness's answer is written to out/result.txt in the session's records directory, exactly as on transport: http.

Step 5 — give the agent tools

The harness's own built-in tools are stripped. Murmur stands up a loopback tool server for the run, hands the driver its address and the capsule's tool names, and the driver points the harness at it — so the model is offered the capsule's tools and nothing else, and Murmur executes every call it makes.

Two kinds of entry reach the model this way:

Declared as Reaches the model as
A runtime: tool artifact, such as murmur/tool-read One tool per artifact, under the part of its name after the /: tool-read
A binary in capabilities.shell.allow One tool per binary, running in the capsule workdir

runtime: driver and runtime: hook artifacts are not offered to the model, on this transport as on the other.

Murmur runs the harness's tool calls concurrently, up to the concurrent tool call limit, so a long shell command does not hold up the other calls the harness makes while it runs.

If the harness stops waiting for a call and closes its connection, a shell command that call started moves to the background at once, whether or not lifecycle.shell_grace_secs is set: the agent can read it through background-command when lifecycle.shell_grace_secs is set, and its result also arrives later as a completion task. trace.jsonl records a harness_note for the call.

Every call the harness makes is checked before it runs, in the same order as on transport: http:

  1. The tool's input_schema — refused if the call's input lacks a field the schema lists in required.
  2. capabilities.filesystem.read_only — refused if the call writes a protected path.
  3. An on-tool-call policy hook, where the capsule declares one — refused if the hook denies the call.

A refused call never runs. The model is handed a failed tool result carrying the refusal's reason, and trace.jsonl records the refusal.

Watch what the harness actually called:

mur trace show
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.

The session's harness_start record names the driver, the harness, the absolute path of the binary spawned, the version it reported, and the capsule tool names the bridge offered. It records no argument values: a driver is free to put the tool server's bearer token in one.

Step 6 — continue a conversation

lifecycle.conversation: threaded maps each A2A context to one harness session. The first task in a context starts a session; every later task in that context resumes it, and the harness — not Murmur — carries the history. Murmur writes no conversation.jsonl here.

The mapping lives in a harness-session.json file under ~/.murmur/conversations/. mur run --resume continues from it. The entry is written when a turn ends having produced something — text, a thought, a tool call, or a turn that ran to completion — so a turn interrupted before the harness produced anything leaves nothing behind, and the next task in that context starts a conversation the harness will answer to.

If the harness has lost a conversation Murmur is still naming, the next task in that context fails with E-RUN-036, and every task after it fails the same way. The entry is left exactly as it was: answering from nothing would be memory loss the person never sees. Dropping it is something you ask for, and it is recorded:

mur run --context ctx_0193f2 --forget-session --task "carry on"
curl -s localhost:41234 \
  -H 'content-type: application/json' \
  -H 'A2A-Version: 1.0' \
  -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{"message":{"messageId":"m1","contextId":"ctx_0193f2","role":"ROLE_USER","parts":[{"text":"carry on"}],"extensions":["https://docs.murmur.nexus/reference/agent-card/#murmur-door-v1"],"metadata":{"https://docs.murmur.nexus/reference/agent-card/#murmur-door-v1":{"forgetSession":true}}}}}'

Either way the entry is deleted, the turn runs as a new conversation under the same context id, and the trace records a harness_session_forgotten event naming the context, the id that was dropped and who asked. The flag applies to the launch's first task, and the forgetSession field to the task its message starts; it goes on a message without a taskId. Only this transport has a harness session, so a forget asked of any other capsule is refused: E-RUN-039 from the CLI, -32602 from the door.

Because the harness holds the history, --resume-mode compact has nothing to compact and refuses with E-RUN-037. context.max_tokens and inference.compaction parse but do nothing: the harness manages its own context.

Step 7 — know the three fixed limits

Three limits bound a run, and none is a manifest setting.

Limit Value What it bounds
Inactivity 600 seconds How long a run may go with neither a line of harness output nor a tool call before the harness is killed with E-RUN-035. Any output and any tool call start the window again, so a harness that is working is never interrupted for taking a long time.
Interrupt grace 10 seconds How long a harness that was asked to stop has to end on its own before it is killed.
Concurrent tool calls 16 How many of the harness's tool calls Murmur runs at once. A call beyond that is refused at once with a failed tool result saying nothing ran; it is not queued.

A full manifest

A coding capsule with tool artifacts, a hook and a shell allowlist on this transport:

name: my-coding-capsule
version: "1.0.0"

artifacts:
  - name: murmur/driver-claude-code
    runtime: driver
    version: "0.9.0"
  - name: murmur/tool-read
    runtime: tool
    version: "0.4.0"
  - name: murmur/tool-edit
    runtime: tool
    version: "0.4.0"
  - name: murmur/tool-code-graph
    runtime: tool
    version: "0.7.0"
  - name: murmur/hook-diff-summary
    runtime: hook
    version: "0.13.0"
    capabilities:
      filesystem:
        scope: "."
  - name: murmur/tool-corpus
    runtime: tool
    version: "0.9.0"
    capabilities:
      state: {}
    config:
      config_version: 1
      read_recent: { default: 10, max: 50 }
      search: { default_k: 5, max_k: 25 }
      prefix_map: { session-note: snt }
      types:
        note:
          schema_version: 1
          schema:
            type: object
            required: [text]
            properties:
              text: { type: string }
              tags: { type: array, items: { type: string } }
            additionalProperties: false

capabilities:
  env:
    allow:
      - HOME
      - PATH
  shell:
    allow:
      - bash
      - git
      - cat
      - ls
      - head
      - tail
      - wc
      - grep
      - find
      - sed
      - diff
      - cp
      - mv
      - rm
      - mkdir
      - python3

inference:
  transport: process
  max_turns: 100
  system_prompt_file: ./instructions.md
  driver:
    artifact: murmur/driver-claude-code

lifecycle:
  task_acceptance: queue
  queue_depth: 8
  after_task: sleep
  shell_grace_secs: 600
  conversation: threaded

It declares no compaction hook and no context.max_tokens: both are inert under this transport, and a manifest that named them would promise a mechanism that never runs.


Summary

Manifest setting Effect
inference.transport: process Drives a harness CLI as a subprocess instead of calling a provider API
inference.driver.artifact Required — names the process driver for that harness
inference.command Overrides the executable the driver's describe() names
capabilities.env.allow The whole environment the harness sees; every variable the driver requires must appear, or the launch is refused with E-CAP-019
capabilities.shell.allow Each binary becomes one tool the model may call through Murmur
A tool's input_schema required list, capabilities.filesystem.read_only, an on-tool-call policy hook All three govern every tool call the harness makes, the same way they govern one on transport: http
lifecycle.conversation: threaded Each A2A context resumes one harness session; the harness holds the history
inference.max_session_tokens Enforced against the harness's own reported counts; the run stops at the first turn that reaches it
context.max_tokens, inference.compaction Parse but do nothing; the harness manages its own context
Inactivity limit, 600 seconds Fixed; a silent harness is killed with E-RUN-035
Interrupt grace, 10 seconds Fixed; a harness asked to stop is killed after it
spend.machine_tokens_per_day Counts this capsule's turns too, from the same reported counts
Token counts in the trace The harness's own report, relayed by the driver; absent for a driver that reports none