Environments
An environment is the model of the network the engine drives against: which network functions are in scope, and the wire-level transports they speak over. This page covers the conceptual shape — the two-table layout, NF roles, and how the engine validates a flow against it.
The two-table model
Every environment has exactly two top-level tables:
nfs— what NFs exist, what role each one plays, which transport each one uses.transports— wire stacks, indexed by ID. Each one declares one protocol stack plus the local + peer details that protocol needs.
NFs reference transports by ID. Multiple NFs can share a transport (two MMEs on one Diameter peer pool). Most labs use one NF per transport for clarity.
This split is deliberate. The same Diameter transport can serve multiple Diameter-side NFs without duplicating peer tables; the same gNB-properties block can pair with different NGAP transports across environments.
A typical multi-NF environment, visualised — two gNBs sharing one NGAP transport, plus an MME on its own Diameter transport, plus a remote UDM on SBI:
NF roles
Thirteen roles ship today, covering 5G core, 4G EPC, and a generic external escape hatch:
Each shipped flow declares the role it simulates. The engine validates this against the environment at startup: a flow with nf: gnb won't run against an environment that declares no gNB. Mistakes fail fast at engine init, not mid-flow.
external is the convention for non-3GPP peers — vendor admin APIs, k8s control planes, internal management endpoints. The shipped REST flows use it.
Role-specific properties
Two NFs need additional identity beyond name + transport:
gNB carries the identity it advertises in NGSetupRequest:
AMF carries the identity it advertises in NGSetupResponse:
Other roles (MME, UDM, AUSF, PCRF, …) don't need a properties block — their identity is fully captured by the transport's protocol-specific config.
Transport blocks per protocol
Each protocol has its own sub-block.
- NGAP —
mode: client | server,local_sctp,local_gtpu,peers: [{ address, port }]. - Diameter — RFC 6733 shape: a
localidentity, apeerstable, aroutesrealm-based routing table. - SBI — HTTP/2 client (
base_url) or server (listen.addr/port) + optionaltls,oauth2. - REST — same shape as SBI without OpenAPI typed bodies + per-binding
auth: { type, token_env, … }. - PFCP — UDP/8805;
mode: client | server,local,peer,node_id.
Full field tables live in the Environment schema reference.
Validation at load
When the engine loads an environment, it cross-checks every NF reference:
- Every
nfs[].transportmust point to an existingtransports[]ID. - Every transport's
protocol:must match exactly one populated sub-block (e.g.protocol: ngaprequiresngap:set;diameter:must be empty). - gNB-role NFs must have an NGAP transport. AMF-role NFs same.
- A flow loaded against this environment must have an NF of the role it declares.
Failures fail-fast at startup, not mid-run. CLI prints the offending field and line.
Multi-NF environments
A single environment can declare every NF the daemon needs. Example: simulate two gNBs sharing one NGAP transport, plus an MME on Diameter, plus a remote UDM on SBI — see config/lab-multinf.yaml.
Multi-protocol flows drive multiple transports on a single UE — see Multi-protocol flows guide.
Where environments live in the daemon
In CLI mode, the environment is -c <file> — read once, discarded after the run. In daemon mode, environments are persisted in the control plane and sent to the agent as job payload. Schedules and on-demand runs reference an environment by id.
The same YAML schema applies in both modes — the control plane stores it as the body of an environment row.
Where to go next
- Configuring environments guide — practical how-to with examples per protocol
- Environment schema — exact field tables for every transport and role
- Subscribers — how UEs attach to the NFs an environment declares
- Templates repository — the shipped flows and suites; lab configs stay under
runner/config/in the d3x monorepo. See the flow & suite catalog for the full list