Authoring Playbooks
Slash-command markdown grammar — phases, step kinds, per-kind params, when clauses, and the Visual ↔ Source round-trip
Authoring Playbooks
A playbook is written as markdown with slash-commands. The same document is parsed server-side into a structured step tree and rendered in the authoring wizard's Visual canvas; analysts can flip between Visual and Source freely without losing information.
Minimal example
# Credential reuse · offboarded / dormant account
Agent-first identity investigation for the pattern
"account that shouldn't be active is active."
## Triage
/enrichment: HR status for the user
system: workday
employee: {{entity.user}}
fields: status, end_date, manager, team
/query: last 30d of auth events for this user
from: auth-events
window: 30d
where: user = {{entity.user}}
## Investigate
/decision: is this a live user account?
options:
- yes · employed and active
- no · offboarded
- partial · on leave / role changed
note_required_on: no, partial
## Contain
/action: rotate or disable the compromised credential
kind: iam.key_rotate
target: {{entity.user}}
danger: medium
approval: required
when: d1 in [no, partial]Grammar
Title (#)
The first # Heading becomes the playbook title. Exactly one title per doc.
Phases (##)
## Heading starts a phase. The Build canvas maps headings to the four canonical phase buckets — Triage, Investigate, Contain, Close — using keyword matching:
| Heading keyword | Phase bucket |
|---|---|
triage | Triage |
investigate, analyze, decide | Investigate |
contain, respond, action | Contain |
close, review, wrap | Close |
Phases can be nested conceptually (you can write ## Phase 1 · establish state — the keyword "establish" matches nothing, so it becomes an unphased block at the top of the canvas). Unphased steps always render above any phased steps.
Steps (/kind:)
/kind: label starts a step. The label is the one-line summary shown at the top of the card; per-kind parameters live on indented key/value lines below.
Seven step kinds:
| Kind | What it represents |
|---|---|
/query | Pull data from a source (e.g. auth-events) |
/pivot | Branch the investigation on an entity |
/enrichment | Look up external context (HR, WHOIS, IPInfo…) |
/decision | Ask the analyst to choose among options |
/action | Mutate the world (rotate a key, isolate a host) |
/review | Human confirmation step |
/note | Freeform note / instruction |
Step parameters
Indented key: value pairs under a step become step.params. Values are stored as strings — the parser does not type-coerce. List values use YAML-style dashes:
/decision: is this a live user account?
options:
- yes · employed and active
- no · offboardedwhen: clauses
Any step can carry a when: clause that gates its execution on prior decisions:
/action: rotate or disable the compromised credential
when: d1 in [no, partial]d1 refers to the first /decision step's answer key (the first word of the chosen option). Supported forms:
d1 = yes— equalityd1 in [yes, partial]— set membership
Special params
These are lifted out of params into first-class fields on the step:
| Param | Moved to |
|---|---|
options: | step.options |
note_required_on: | step.note_required_on |
id: | step.id (overrides the auto-generated id) |
when: | step.when (parsed) |
Per-kind parameters
UDM fields in examples below are the real flat snake_case identifiers from the nano schema. See UDM Fields for the full set.
/query
| Param | Meaning |
|---|---|
from | Source dataset id (auth-events, edr-network, …) |
window | Time window (24h, 7d, 30d) |
where / filter | Boolean expression over UDM fields |
/query: cloud API calls from this user in the last day
from: cloudtrail
window: 24h
where: user = {{entity.user}} AND source_type = "cloudtrail"/pivot
| Param | Meaning |
|---|---|
on | UDM field to fan out on |
keys | Optional sub-specifier |
/pivot: per-source-IP summary
on: src_ip/enrichment
| Param | Meaning |
|---|---|
system | External system id (workday, ipinfo, virustotal) |
field | What to look up |
| Any additional keys are passed verbatim to the enrichment call |
/enrichment: HR status for the user
system: workday
field: employment_status
employee: {{entity.user}}/decision
| Param | Meaning |
|---|---|
options: (list) | Answers the analyst chooses from |
note_required_on | Comma-list of answer keys that force a note |
The first word of each option is the answer key used by downstream when: clauses.
/action
| Param | Meaning |
|---|---|
kind | Dotted action id (iam.key_rotate, edr.isolate_host) |
target | Entity / identifier the action hits |
danger | low · medium · high — drives approval routing |
approval | auto · required — inline override |
reversible | Boolean hint for the operator |
Every /action step renders as a manual checkbox in the active-run view
regardless of the approval setting. Outbound EDR / IAM / firewall
connectors aren't wired yet — when they land, approval: auto will fire
automatically for non-high-danger actions. danger + approval are
stored today so playbooks are ready for that switch.
/review
| Param | Meaning |
|---|---|
criteria / hint | What the analyst is confirming |
approver | Optional role or user id that must sign off |
/note
No params — just a freeform instruction block. Parser emits kind: "note" with the label as the body.
Visual ↔ Source round-trip
The authoring wizard's Build phase exposes both views. Switching is lossless:
- Visual → Source — the draft's flat step list is re-emitted as slash-command markdown, preserving phase headings via the keyword mapping above and only including params with non-empty values.
- Source → Visual — the textarea is parsed via the same parser the server uses; the resulting step tree is flattened into the canvas, tagging each step with the phase its heading mapped to.
Short hints that describe what to type (format reminders, example values) render inline beneath the field label in the Visual form. Longer explanatory copy lives behind a ? tooltip icon on the label — the redesign convention.
Validation
Each kind has a soft validator that surfaces a NEEDS INPUT chip on the step card when a required param is missing:
| Kind | Required |
|---|---|
/query | from |
/pivot | on |
/enrichment | system + either field or key |
/decision | at least 2 options |
/action | kind, target, danger |
/review | criteria or hint |
/note | a non-empty label |
Validation does not block publishing — it signals the author — and the counter in the Build toolbar rolls up into the Review phase's policy-check section.