Skip to main content
Docs navigation

Runbooks

Build a procedure from declared actions and publish it only when every target resolves. A run executes one frozen plan through policy and approval, and stages stop at the first failure. Each command, output, and decision remains reviewable.

Before you start, you need:
  • At least one runner enrolled in the account that the runbook can target — by name, or through a group.
  • The required action packs installed and trusted on those runners.
  • Permission to manage or run runbooks. If policy requires approval, an approver whose runner scope covers the complete plan.

The shortest path#

  1. Create a draft in Runbooks, or import a canonical JSON definition.
  2. Add operator context and run-time inputs, then define each step's action, arguments, outputs, success conditions, and optional wait.
  3. Select the target scope, then choose an action available across that complete scope.
  4. Publish after the current definition and live preflight both pass.
  5. Supply the run-time values and reason, review the resolved plan, and start it.
  6. Collect the required approvals for the complete execution, then review each result.

1. Create or import a draft#

Open Runbooks and choose New runbook. Start with a short title, a stable slug, and the operator context: prerequisites, stop conditions, ownership, the expected outcome, and rollback instructions. Context supports headings, paragraphs, lists, and fenced code. Links, images, and raw HTML stay inert.

If another tool or an LLM already produced Definition v1 JSON, choose Import runbook. Drop or select a .json file, or paste the document. An import always becomes an editable draft. It never publishes or runs automatically.

emisar console · Import runbook
Import a file or paste the same canonical JSON used by the console and MCP.

2. Define the workflow#

Run-time inputs#

Declare only the values that change between executions. An input can be a string, integer, number, boolean, or enum. Make it required, constrain its range or length, or give it a same-type default. An integer is a whole number, and a number can contain a decimal. Sensitive inputs have no stored default and show as [REDACTED] in plans, approvals, and results.

emisar console · Run-time inputs
The example accepts one validated configuration path and supplies a safe default.

Stages and steps#

A runbook executes stages from top to bottom. A stage groups work behind one barrier. A step declares one action, its runner or group targets, argument bindings, retained outputs, success conditions, and an optional wait policy.

  • Sequential finishes one step across all selected runners before the next step starts.
  • Parallel makes all stage items eligible together. Maximum concurrency limits the number of active attempts. Use 1 for one-host-at-a-time maintenance.
  • Every stage is a barrier. The next stage starts only after every item in the current stage succeeds.

Select targets before choosing the action. The action menu contains only work currently available across the complete selection. The editor follows the action to its pack. You do not pin a pack version in the definition.

emisar console · Stage editor
The reload stage targets the edge group, with one attempt active at a time in this draft.

Target selection#

A step can run on one named runner, every online runner in a group, or one online runner from a group. Use a runner name for host-specific work, the complete group for fleet changes and checks, and one group member for representative diagnostics or a canary.

  • Runner name runs the step on that specific runner.
  • All available runners in group runs the step on every online runner in the group.
  • One random runner picks one online runner from the group.

The one-runner pick happens once, during preflight, after emisar checks the action, trusted pack, and contract on every online group member. The start page and the execution both use that same runner — if it disconnects before dispatch, the execution stops, and the work does not move to another member.

emisar console · Target selection
Choose every online runner in a group, one online member, or a named runner from the same picker.
Target forms
{"selection":"all","refs":["runner:nomad-hvn02~0123456789abcdef0123456789abcdef"]}
{"selection":"all","refs":["group:va1-nomad"]}
{"selection":"random_one","refs":["group:va1-nomad"]}

Action arguments#

Each action argument receives one complete JSON value. Omit an optional argument, enter a literal, bind a declared run-time input, or use a named output from an earlier stage. There is no string interpolation. The editor checks the bound value against the trusted action schema before publication and again when it resolves a run.

emisar console · Action arguments
The reload step binds the action's file argument to the config_path run-time input.

Extracted outputs#

Retain only the values that later work or a success condition needs. Choose stdout or stderr, then extract with JSON Pointer, contains, literal grep, or a bounded regular expression. Give each value a stable output ID. Mark an output sensitive to mask it in results and evidence. Conditions and compatible sensitive arguments in later stages still use the real value.

JSON Pointer reads a field from a complete JSON document, such as /healthy. The selected stream must contain valid JSON; surrounding log lines are not ignored. Invalid JSON, a missing field, or incomplete text output causes extraction to fail.

For an action with an output schema, the editor uses its schema-validated stdout result when you configure a stdout JSON Pointer. The runner validates redacted stdout against that schema, and emisar validates the result again before recording success. This result is called structured_output in the JSON definition; it comes from stdout, never stderr. Text extractors read the selected stream directly. Existing bindings are preserved when you reopen a runbook.

When authoring JSON through an agent, inspect action.output_schema in the get_action response. For a new stdout JSON Pointer binding, use structured_output when that schema is present; otherwise use stdout. Structured output supports JSON Pointer only. JSON Pointer can also read stderr JSON; text extractors always use stdout or stderr. Keep existing source values during unrelated edits.

emisar console · Extracted outputs
The health check retains only the healthy field from schema-validated stdout.

Success conditions#

An action that exits successfully counts as a successful step unless you add conditions. Conditions read named extracted outputs. Every condition must pass. Compare with equality, ordering, containment, membership, or a bounded regular expression. Add the output first. A condition cannot refer to an undefined value.

emisar console · Success conditions
The upstream check succeeds only when its extracted healthy value is true.

Wait policy#

When a successful observation does not yet meet its conditions, a wait can run the same observation again after a fixed interval. Bound it with both a timeout and a maximum number of observations. A wait never retries an action failure, denial, refusal, extraction error, timeout, or cancellation. Those events halt the execution.

emisar console · Wait policy
The health check observes again every 10 seconds, for at most 12 observations or 120 seconds.
Identifiers are references, not labels.
Input IDs name run-time values. Step and output IDs form prior-output references such as check_health.status. Stage and step IDs also remain stable in the frozen plan, result, and audit trail. Titles are the human-readable text you can revise.

3. Publish a resolvable release#

Save whenever you need to keep incomplete work. Use Test draft to try valid unpublished changes. Publishing has a higher bar: the strict definition must be valid, every target must resolve in your scope, each selected runner must expose one compatible action contract, and the current trusted pack and policy plan must resolve. The editor lists every issue beside Publish check and disables Publish until the check passes.

Publish shows exactly what changes from the live definition, then mints the next release: slug@1, slug@2, and so on. The Run action uses the published release; later edits stay separate until you publish again. Test draft uses those unpublished changes instead. Every execution keeps the exact definition and plan it started with. New executions check the current runners, actions, packs, and policy again.

emisar console · Publish
Confirming a release shows the lines it changes against what runs today.

4. Review and start the run#

Open Runbooks and choose Run on a runbook with a live release. Supply its run-time inputs and a reason for running it now. Before Start runbook becomes available, emisar resolves the exact runner for every item, plus the current trusted pack, content hash, action arguments, policy decisions, and stage concurrency.

Review the displayed release and plan before you start. Start checks the current runners, action contracts, and policy again. If the plan changed, emisar shows the updated plan for another review without starting an execution. Publishing a newer release does not replace the release already open in your Run page. When a step picks one runner from a group, the plan shows which runner it picked and which group that runner came from. After the run starts, the plan does not change: emisar does not add or replace a target, it uses your arguments exactly as you entered them, and every step stays in its stage.

emisar console · Start execution
The start page summarizes the resolved work and whether one run-level approval will be required.

5. Approve the execution#

Runbooks use the same account policy as direct actions. If any item requires approval, emisar opens one approval request for the complete frozen execution. Policy may require more than one approver. The approver reviews every stage, action, runner, visible argument, and risk in one place. Approval does not create a second approval for each action.

No action run is created before approval. The decision applies only to this execution, and the approver's runner scope must cover the complete plan. Current access, policy, pack trust, action compatibility, and runner state are checked again before dispatch.

emisar console · Runbook approval
Each approval covers the complete frozen plan; the action rows keep targets and visible arguments close to the work being approved.

6. Read the result#

The execution page follows the same stage order as the plan. Each item shows the action, risk, runner, step ID, outcome, duration, and its bounded command and output. Choose View run for the complete action run when you need event-level detail. Sensitive values remain redacted.

When a step extracts outputs or defines success conditions, the result also keeps the extracted values, integrity digest, condition evidence, observation attempts, and wait state. This is the evidence to use when deciding whether to continue outside the runbook or start a rollback.

emisar console · Runbook result
Executed items keep commands and bounded output inline; the upstream check also records its extracted value and condition evidence.

Definition v1 reference#

JSON is the only interchange format. The structured editor, import flow, persistence layer, and MCP use the same strict schema_version: 1 object. Unknown fields are rejected. This is a smaller version of the edge rollout used throughout this guide:

Canonical runbook JSON
{
  "schema_version": 1,
  "context_markdown": "## Before you run\n\nValidate the candidate Caddyfile.",
  "inputs": [
    {
      "id": "config_path",
      "description": "Absolute path to the validated Caddy configuration.",
      "type": "string",
      "required": false,
      "sensitive": false,
      "default": "/etc/caddy/Caddyfile",
      "min_length": 1,
      "max_length": 256
    }
  ],
  "stages": [
    {
      "id": "reload",
      "title": "Reload edge configuration",
      "mode": "parallel",
      "max_parallel": 2,
      "steps": [
        {
          "id": "reload_caddy",
          "pack": {"id": "caddy"},
          "action": "caddy.reload_config",
          "targets": {"selection": "all", "refs": ["group:edge-web"]},
          "args": {
            "file": {"source": "input", "ref": "config_path"}
          },
          "outputs": [],
          "success": [],
          "wait": null
        }
      ]
    },
    {
      "id": "verify",
      "title": "Verify the edge fleet",
      "mode": "parallel",
      "max_parallel": 2,
      "steps": [
        {
          "id": "check_upstreams",
          "pack": {"id": "caddy"},
          "action": "caddy.reverse_proxy_upstreams",
          "targets": {"selection": "all", "refs": ["group:edge-web"]},
          "args": {},
          "outputs": [
            {
              "id": "healthy",
              "source": "structured_output",
              "sensitive": false,
              "extract": {
                "type": "json_pointer",
                "expression": "/healthy"
              }
            }
          ],
          "success": [
            {
              "output": "healthy",
              "operator": "equals",
              "value": true
            }
          ],
          "wait": {
            "interval_seconds": 10,
            "timeout_seconds": 120,
            "max_attempts": 12
          }
        }
      ]
    }
  ]
}

Failure and safety#

A denial, refusal, timeout, cancellation, action failure, extraction failure, unmet terminal condition, lost authorization, or unusable frozen pack halts the execution. Peers already running can settle and report their real outcomes. No new item or later stage starts.

Approval never overrides later drift. Before every physical attempt, emisar rechecks runner access, current membership, policy, pack trust, the action contract, and runner state. Cancelling an execution also cancels pending stages, waits, and active attempts without erasing their history.

Signature-enforcing runners are not supported yet.
Customer-CA signing currently covers exact direct run_action dispatches. A runbook that targets a signature-enforcing runner fails closed with signed_runbook_unsupported; use individually signed actions for that runner.

LLMs and MCP#

An agent reads runbooks with list_runbooks and get_runbook. By default it sees the live release; asking with status: "draft" shows the unpublished change instead.

create_runbook_draft starts a new runbook. update_runbook_draft replaces the one unpublished change, and it must send the definition_sha256 it read — if someone else edited the draft in the meantime, the update is refused instead of overwriting their work. Publishing stays a human step in the console.

execute_runbook runs the live release. To test draft content, the agent must ask for it clearly — allow_draft: true plus the draft's hash — and the test goes through the same policy and approvals as any run without publishing anything. Asking for an older release answers not_live instead of quietly running the current one. While work or an approval remains, the response shows what is still blocking and returns an exact wait_for_run continuation.

For client setup and the complete tool contract, continue to Connect a cloud LLM.

Limits#

What Limit
Definition 64 KiB
Run-time inputs Up to 32
Stages Up to 16
Target refs per step Up to 16
Resolved execution 256 items — one per runner per step
Parallel attempts in a stage 1 to 16 at once — 5 by default
Retained outputs per step Up to 16
Success conditions per step Up to 16
Wait Polls every 5 seconds to 1 hour, 2 to 100 attempts
Frozen execution plan 1 MiB
Execution duration Ends within 24 hours, waits included
Title 80 characters, 320 bytes
Description 4,096 characters, 8 KiB

These are the only limits you need to track. An agent reads a runbook through one MCP response. That response budget comes from the limits above. Anything you can save and publish is something an agent can read and run. If a runbook exceeds the budget, the agent reports that it is too large and directs the user to the console — it never reports that the runbook does not exist.

See the limits reference for the wider API, output, and retention caps.

A runbook runs a known plan.
Use a runbook when the steps, stopping rules, and expected result are known before you start. For open-ended diagnosis, dispatch actions one at a time instead — read each result and choose the next action from what it shows. A runbook cannot change its plan mid-run.

Last reviewed September 8, 2026