Your first YAML flow
By the end of this tutorial you will have copied the shipped registration flow, modified one transition to fail on a different condition, and watched the resulting run land in failed instead of registered. The point is to put your hands on the YAML schema before reading the full reference.
Prerequisites
- You completed the Quickstart and have
d3x-runinstalled. - An AMF reachable from the lab — same as the previous tutorial.
- Your clone of d3x-templates. The examples below assume it sits at
./d3x-templates, the same path the Quickstart used.
Step 1 — Copy the shipped flow
The shipped flows live in the templates repository, one YAML file per flow, grouped by network function. Copy the gNB registration flow into a working directory of your own so your edits don't touch the originals:
Open my-templates/registration_strict.yaml. Notice the shape: kind: flow, type: client, protocol: ngap, nf: gnb, initial_state: idle, final_states: [registered, failed]. Each entry under states: has transitions keyed by event:.
Step 2 — Rename the flow
The templates loader keys flows by name. Change the name: field so your edit doesn't collide with the shipped registration flow:
Step 3 — Tighten a check
In the wait_context_setup state, the shipped flow already asserts that security_key and ue.AmfUeNgapId are both not_empty before sending InitialContextSetupResponse. Add one more check that fails on purpose so you can watch the run land in failed — assert that ue.AmfUeNgapId equals 0, which a real AMF never returns:
When a check fails, the engine fails the flow immediately. It does not abort just the transition and wait — the run completes right away with a result of failed, in well under a second. The on_timeout on this state never fires.
Step 4 — Run your modified flow
Expected — the failing equals check ends the run almost instantly:
The duration is a fraction of a second, not the 10s on_timeout window. The failed check fails the flow on the spot, so the timer on wait_context_setup is never given the chance to fire. Add -trace, as above, to see each check and send logged in order up to the one that fails.
Step 5 — Make it pass again
Change the check you added in Step 3 to one that holds — assert that ue.AmfUeNgapId is non-empty instead of equal to zero:
Re-run the same command. With every check passing, all four actions in the transition fire and the flow lands in registered with a result of PASS.
Step 6 — Inspect the catalog entry
This confirms the flow loaded, prints the YAML, and shows the high-level metadata fields (name, type, protocol, NF, category) that the daemon and web UI surface.
What you built
You wrote your first custom flow without writing Go. You've seen how a single check op (equals, not_empty, greater_than, and the rest) determines whether a transition fires and that a failed check fails the run on the spot. The full schema — every action type, every check op, the template syntax for {{...}} — is in the flow schema reference.
Where to go next
- Build the other side of the wire in your first server flow.
- Work through the full set of editing patterns in writing flows.
- Read how a failed check propagates through the state machine in actions and checks.