Flow schema
Authoritative schema for kind: flow YAML. Every field, every action type, every check op.
This page is the field-by-field reference. For prose on the authoring model, see Writing flows and suites and Flows. The cited template files live in the d3x-templates repository.
Synopsis
Top-level fields
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
kind | string | no | — | flow. Optional but required by the templates loader so it can dispatch to the correct parser. |
name | string | yes | — | Unique flow name. Looked up by run-flow -flow <name>. |
description | string | no | — | One-line summary; shown in the catalog and control-plane UI. |
detail | string | no | — | Long-form description; markdown allowed. |
category | enum | no | — | functional, negative, robustness, stability, lifecycle, load, stress. Filters in catalog views. |
type | enum | yes | — | client or server. Client flows fire Start; server flows wait for first RX. |
protocol | enum | yes | — | ngap, sbi, diameter, rest, pfcp. Routes inbound demux + the default send protocol. |
nf | enum | yes | — | gnb, amf, smf, ausf, udm, pcf, nrf, upf, mme, pgw, pcrf, af, external. Validated against the environment. |
initial_state | string | yes | — | Name of the state the FSM starts in. |
final_states | []string | yes | — | Terminal state names; reaching one ends the flow. |
unsupported_states | []UnsupportedState | no | — | Terminals that mean “this core does not implement the scenario”. Produces verdict unsupported and run-flow exit 3 when every completed UE lands there. Each entry needs state (must be in final_states) and a reason. The state must be reachable by an inbound message, not via on_timeout or Error. |
final_checks | []Check | no | — | Aggregate-metric assertions evaluated once after every UE finishes. A failed final check does not abort the FSM (it has already ended) but makes the verdict fail. |
fuzz | object | no | — | Wire-level mutation of encoded frames. Present = this flow is a fuzz driver. Drive campaigns with d3x-run fuzz; see d3x-run schema for the field set. |
states | map | yes | — | Map of state name → State. |
any_state_transitions | []Transition | no | — | Fallback transitions matched when no state-specific transition does. |
State shape
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
transitions | []Transition | no | — | Event-keyed transitions out of this state. |
on_timeout | TimeoutConfig | no | — | Fallback transition fired when no event matches before duration elapses. |
TimeoutConfig:
| Name | Type | Description |
|---|---|---|
duration | duration | How long to wait before firing |
target | string | Target state |
actions | []Action | Run on the timeout transition before target is applied, with the same execution semantics as transitions[].actions. Lets a flow express "wait N then send X" for token-expiry or post-idle re-probe tests. |
For the conceptual model behind states and transitions, see States and transitions.
Transition shape
| Name | Type | Required | Description |
|---|---|---|---|
event | EventDef | yes | Trigger; see Event shape |
target | string | yes | Target state name (or _self in any_state_transitions) |
actions | []Action | no | Operations to execute when this transition fires |
Event shape
event: accepts three forms.
Authors may prefix any event name with the protocol (ngap.X, diameter.X, sbi.X) for visual disambiguation in multi-protocol flows. The matcher strips a known protocol prefix on either side before comparing; an unknown prefix such as weird.X is left intact and will not match a bare event.
Synthetic events:
| Event | Source | Notes |
|---|---|---|
Start | engine | Fired once per UE for client flows out of initial_state |
Error | protocol | Fired on decode error, enricher error, or unrecoverable failure |
StateTimeout | engine | Fired when an on_timeout block elapses |
UplaneComplete | engine | Fired after a uplane_start action finishes its run |
Action types
Action types: send, parallel_send, check, extract, uplane_start, ngap_realloc, ngap_handover_swap, cmd. All actions execute in YAML order within a transition, with the constraint that check and extract must not appear after a send / parallel_send. For when to reach for each, see Actions and checks.
send
Emit a message on the wire. Required when the transition needs to TX.
| Field | Type | Required | Description |
|---|---|---|---|
type | send | yes | — |
message | string | yes | Registered enricher name; see Enricher catalog |
message_body | JSON string | no | Pre-populated fields on the typed message struct, merged before the enricher fills the rest |
params | map | no | Runtime params overlay |
protocol | enum | no | Override the default protocol (per-action; used in multi-protocol flows) |
peer | string | no | Override the target peer name within the protocol |
check
Assert a field on the most recent inbound message (or ue.<path> against UE context). Must precede any send in the same transition.
| Field | Type | Required | Description |
|---|---|---|---|
type | check | yes | — |
field | string | yes | Field path; ue.X reads UE context, otherwise reads the most recent RX message |
op | enum | yes | See Check ops |
expected | any | depends | Required for ops that compare against a value |
extract
Read a field off the most recent inbound message and store it in ue.Params[<store>] for later template lookup. Must precede any send.
| Field | Type | Required | Description |
|---|---|---|---|
type | extract | yes | — |
field | string | one of | Shorthand: field path to read from the inbound message. Pair with store. |
value | string | one of | Shorthand: a template-derived value, used instead of field. Set field or value, never both. |
store | string | yes (shorthand) | Shorthand: target key in ue.Params |
extracts | list | one of | List form: multiple {field|value, store} entries |
uplane_start
Arm the user-plane traffic generator after the NGAP send that surfaced the UPF tunnel parameters. Reads parameters from the gNB's uplane: config block. Allowed between sends.
ngap_realloc
NGAP-only. Reallocate RAN-UE-NGAP-ID on the same gNB before the next send. Used in stress flows.
ngap_handover_swap
NGAP-only. Move the UE from the source gNB to the target gNB after HandoverCommand. Used by templates/gnb/handover_source.yaml.
parallel_send
Fire N copies of one send concurrently. Must be the only send-type action in the transition.
| Field | Type | Required | Description |
|---|---|---|---|
type | parallel_send | yes | — |
message | string | yes | Registered enricher name |
count | int | yes | Total number of requests to fire (≥ 1) |
concurrency | int | no | Max in flight at once. 0 = unbounded, capped at count |
cmd
Run a local program synchronously. There is no shell — argv is a list, with no word-splitting or globbing. Off unless the process was started with -allow-cmd-actions.
| Field | Type | Required | Description |
|---|---|---|---|
type | cmd | yes | — |
argv | []string | yes | Program + arguments as a list. Every element is template-expanded. |
cwd | string | no | Working directory. Relative paths resolve against the flow YAML's own directory (also the default). |
env | map | no | Environment overlay on the runner's own env (values are template-expanded). |
timeout | duration | no | Kill the process on expiry (default 10s). |
ignore_error | bool | no | A non-zero exit or timeout is logged instead of failing the UE. |
store_stdout / store_stderr | string | no | Publish captured output into ue.Params under this key. |
Check ops
| Op | Required expected | Passes when |
|---|---|---|
equals | yes | Field equals the expected value |
not_equals | yes | Field's string form differs from expected |
not_empty | no | Field resolves and is non-zero |
greater_than | yes | Field > expected |
less_than | yes | Field < expected |
greater_or_equal | yes | Field ≥ expected |
less_or_equal | yes | Field ≤ expected |
contains | yes | Field's string form contains the expected substring |
not_contains | yes | Field's string form does not contain the expected substring |
exists | no | Field resolves (regardless of value) |
regex | yes | Field's string form matches the RE2 pattern in expected |
in / oneof | yes | Field's string form is a member of the expected set |
length | yes | Length of the resolved value equals expected |
ie_present | no | Field resolves and is non-nil/non-empty (stricter than exists) |
Template expressions
message_body JSON, params values, and expected values may contain {{...}} expressions. Two forms.
Whole-value templates ("{{x}}") preserve the resolved value's native type. String interpolation ("prefix-{{x}}-suffix") returns a string. Compilation happens at flow-load time; unknown function names and arity errors fail fast.
Resolution scopes:
ue.X— UE context fields (PascalCase)params.X— per-flow params (params:on a suite step, or the send action's ownparams)- bare names — fields on the most recent inbound message
Enricher catalog
Each enricher binds one message label to a built-in encoder. The FSM author references the label by name in send.message. The tables below are a representative subset. Dump the live catalog — every enricher name, plus accepted params, check ops, and metric keys — with d3x-run vocab.
NGAP
| Message | Prototype |
|---|---|
| NGSetupRequest | NGAPNgSetupRequest |
| InitialUEMessage | NGAPInitialUEMessage |
| AuthResponse | NGAPNASUplinkNASTransport |
| SecurityModeComplete | NGAPNASUplinkNASTransport |
| InitialContextSetupResponse | NGAPInitialContextSetupResponse |
| RegistrationComplete | NGAPNASUplinkNASTransport |
| ConfigurationUpdateComplete | NGAPNASUplinkNASTransport |
| PDUSessionEstablishmentRequest | NGAPNASUplinkNASTransport |
| PDUSessionResourceSetupResponse | NGAPPduSessionResourceSetupResponse |
| DeregistrationRequest | NGAPNASUplinkNASTransport |
| UEContextReleaseCommand | NGAPUEContextReleaseCommand |
| UEContextReleaseComplete | NGAPUEContextReleaseComplete |
| PDUSessionReleaseRequest | NGAPNASUplinkNASTransport |
| PDUSessionResourceReleaseResponse | NGAPPduSessionResourceReleaseResponse |
| NGReset | NGAPNGReset |
| ServiceRequest | NGAPInitialUEMessage |
| UEContextReleaseRequest | NGAPUEContextReleaseRequest |
| UEContextSuspendRequest | NGAPUEContextSuspendRequest |
| RANConfigurationUpdate | NGAPRANConfigurationUpdate |
| MalformedInitialUEMessage | NGAPInitialUEMessage |
| HandoverRequired | NGAPHandoverRequired |
| HandoverNotify | NGAPHandoverNotify |
Diameter
S6a: ULR, ULA, ULAUserUnknown, ULALoopDetected, AIR, AIA, PUR, PUA, IDR, IDA, CLR, CLA, NOR, NOA. All on DiameterMessageProto.
Gx: CCRInit, CCRTerminate, CCA.
Rx: AAR, AAA.
SBI
UDM: Nudm_SDM_GetSubscriptionData, Nudm_SDM_GetSubscriptionData_Answer, Nudm_UEAuthentication_GetAuthData, Nudm_UEAuthentication_GetAuthData_Answer.
AUSF: Nausf_UEAuthentication_Authenticate, Nausf_UEAuthentication_Authenticate_Answer.
AMF: Namf_Communication_N1N2MessageTransfer, Namf_Communication_N1N2MessageTransfer_Answer.
PFCP
PFCPHeartbeatRequest, PFCPHeartbeatResponse, PFCPAssociationSetupRequest, PFCPAssociationSetupResponse, PFCPSessionEstablishmentRequest, PFCPSessionEstablishmentResponse, PFCPSessionModificationRequest, PFCPSessionModificationResponse, PFCPSessionDeletionRequest, PFCPSessionDeletionResponse.
REST
Generic — flow authors declare the message label inline. The loader registers the label at flow-load time. The shipped catch-all sentinels are RESTGeneric and RESTGeneric_Answer.
Examples
Minimal client flow
Server flow
Notes
- A check action that fails aborts the transition; the FSM stays in the current state.
- Send actions cannot precede checks within the same transition. The validator rejects this at flow-load.
- Server flows must NOT have a
Start-event transition atinitial_state— they're reactive. - Client flows MUST have a
Start-event transition atinitial_state. _selfis allowed only as the target of anany_state_transitionand means "stay in the current state".
Where to go next
- Writing flows and suites — authoring walkthrough.
- Actions and checks — when to reach for each type.
- CLI reference —
vocab,schema,lint, anddescribe.