Skip to content

Capsules

A Capsule is Murmur's primary execution abstraction.

A capsule launches from a manifest, installs its declared artifacts, executes inside a constrained environment, writes outputs, and exits. The manifest is the complete configuration.

Agent capsules

An agent capsule runs the LLM inference loop built into the runtime. It is defined entirely by its murmur.yaml: an inference: block in the manifest is what puts the capsule in agent mode.

# minimal agent manifest
name: my-agent
version: "0.1.0"

artifacts:
  - name: murmur/driver-anthropic
    version: "0.19.0"
    runtime: driver
    gateway:
      endpoint: https://api.anthropic.com
      api_key: ${ANTHROPIC_API_KEY}

capabilities:
  shell:
    allow:
      - bash

inference:
  transport: http
  model: claude-sonnet-5
  driver:
    artifact: murmur/driver-anthropic

mur build packages this as a manifest-only .mur.zip.

Script capsules

A script capsule runs its own code instead of the agent loop. Its manifest has no inference: block, and its root holds one .wasm component, compiled against the capsule world, that exports murmur:capsule/run. The runtime calls run once; the session ends when it returns.

The component can Through
Call a tool the manifest declares under artifacts: murmur:tool-registry/invoke
List, describe, search, pull and remove artifacts murmur:artifact-manager/manage
Send a task to a peer capsule murmur:message/send
Read and write its work folder, and read the runtime directory at murmur-runtime/ WASI
Reach the hosts capabilities.network.allow lists wasi:http

It reads its task from murmur-runtime/task.md and writes its result to out/result.txt in its work folder.

A script capsule leaves the record an agent session leaves:

  • its trace.jsonl opens with session_start and holds one tool_call per invoke, and one artifact_pulled or a2a_send line per pull or send, each written as it happens
  • its session_end says how run ended: ok, trapped, deadline_exceeded, resource_limit or failed — see script capsule endings
  • while it runs it holds a running-capsule record with no address, so mur ps lists it and mur-roost counts it
  • with observability.otel_endpoint set, it posts one capsule.session span and one capsule.tool_call span per invoke

No hooks run in a script capsule, and the wasi:http requests it makes itself are not traced.

Isolation model

Murmur uses the WASM Component Model as the primary execution boundary:

  • WASM components run in a sandboxed runtime
  • WASI capabilities explicitly control filesystem and network access
  • Manifest capability grants are part of the security policy

Why WASM

WASM enables consistent execution contracts and explicit capability boundaries, while still allowing portable packaging and composable interfaces between runtime components.

Lifecycle

Typical lifecycle:

  1. Launch capsule
  2. Read manifest
  3. Resolve and install artifacts
  4. Execute workload
  5. Write outputs/logs
  6. Exit

Capsules are designed to be fast-starting and horizontally scalable.

Every capsule goes through the same runtime, which turns its manifest into a running process:

  • Parses capsule and artifact metadata
  • Resolves dependencies from the project store, then the global store
  • Configures capability grants (filesystem, network, shell) from the manifest (see Access control)
  • Links and invokes runtime interfaces for tools and artifact management
  • Captures outputs and logs in a predictable workspace layout

Execution limits

Every component call the runtime makes — a capsule run, a tool or driver run, and each hook lifecycle call — is bounded by two independent limits:

Limit Bounds What happens at the limit
Deadline Wall-clock time for one component call A call that never returns is stopped with an error instead of hanging the session
Resource limiter Memory growth, table growth, and instance count A call that tries to grow past its cap is stopped with an error instead of consuming host memory without bound

Both are set per-manifest under capabilities.limits (see Manifest Schema). Omitting them applies generous built-in defaults — a manifest that says nothing gets the defaults, not unlimited resources. The default deadline for a hook lifecycle call is 30 seconds, against 600 for a capsule, tool or driver call, so one wedged hook cannot stall a session for the capsule-wide budget on every event; a declared deadline_seconds replaces both defaults and applies to every component call, hooks included. A hook's own entry can set deadline_seconds for that hook alone, ahead of the capsule-wide value. Hitting either limit is reported distinctly from an ordinary crash. On the hook path it is handled like any other hook error: logged, and the runtime moves on to the next hook.

The deadline bounds the whole call in wall-clock time, whether the component is computing or waiting on the runtime — a sleep, or a driver awaiting a provider response that never comes:

The call is Stopped at
Running its own code The deadline
Waiting on the runtime The deadline plus a 2-second grace

A call stopped while waiting is recorded in the trace as a call_deadline line naming the component and the call. A hook stopped this way is not called again for the rest of the session: each later event it is bound to is logged as not called.

Runtime identity

Every running agent capsule gets a capsule name and version (from murmur.yaml), a runtime-generated session id, and a capsule URL (an OS-assigned local listener port). The same identity is exposed in MURMUR.md, in the environment the driver and WASM tools see, and in every shell command's environment — so the agent, its tools, and its hooks all see one consistent identity for the session.