sim_source
Commercial gear, licensed separately from the engine. The engine, the SDK and every other gear in this catalog are Apache 2.0.
sim_sourceis published under the Business Source License 1.1: free to read, copy and use non-production; production use, including your own traffic, needs a commercial licence. Source influxrig-ent/pkg/gears/sim_source/, a separate repository from the engine. See the enterprise introduction and the worked tutorial.
The sim_source gear generates ISO8583 traffic from a spec's simulation section. It is a source gear and a pure logic gear: it emits messages into the pipeline rather than processing incoming ones, and it resolves field values only. Like every logic gear, it never touches the wire format itself; a codec_iso8583 gear downstream packs the bytes, exactly as it would for a real counterparty's traffic.
| Attribute | Details |
|---|---|
| Source Code | pkg/gears/sim_source |
| Pairs With | codec_iso8583 (encode), typically followed by io_tcp or io_iso8583 to reach the wire |
| Port OUT | Fields-only ISO8583 message |
| Port OUT Cardinality | Single |
| Always Emitted Metadata | iso8583.mti, sim.seed |
| Conditionally Emitted Metadata | None |
| Mandatory Consumed Metadata | None (source gear: no input port) |
| Optional Consumed Metadata | None |
| Signals Sent | None |
| Signals Subscribed | sim.start, sim.stop, sim.rate, sim.reset (Control Plane) |
Reference
The identity, ports, and configuration below are generated from the gear's manifest, so they stay in lockstep with the code.
| Type | sim_source |
| Category | logic |
| Status | stable |
| Terminus | io |
Generates ISO8583 traffic from a spec's simulation section with deterministic macros and configurable rate shaping.
Ports
| Port | Direction | Role | Summary |
|---|---|---|---|
out | output | egress | Generated ISO8583 messages emitted into the pipeline. |
Configuration
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
seed | integer | yes | - | Random seed for deterministic generation. Mandatory for reproducibility. |
spec | string | yes | - | Spec reference (store URN or file path). Example: iso8583-v87-ascii:v2.2.0 |
defaults | object | - | Field defaults. Keys can be field numbers ("2") or aliases ("card.pan"). | |
expired_percent | integer | 0 | The share of generated expiry dates, from 0 to 100, that are already expired: between one and expiry_months months before the current one. | |
expiry_months | integer | 24 | How far ahead an expiry date (a field whose layout is YYMM, DE 14) is generated, in months from the current one. | |
mix | array | - | Traffic mix: a list of {use, weight} entries overriding the spec's simulation.mix. | |
rate | object | - | Rate shape: shape (constant, ramp, poisson [Roadmap], spike [Roadmap]), tps, from, to, over, duration. | |
schedule | string | - | Reserved for the on_schedule trigger; parsed but unused today. | |
set | object | - | Pinned field values (highest precedence). Keys can be field numbers or aliases. | |
templates | object | - | Per-MTI field templates. Map: MTI -> (field -> macro/literal). | |
timezone | string | local | The clock that $NOW and $RRN render: local, GMT (or UTC), or a zone name such as America/Montevideo. | |
trigger | enum: on_load, on_control, on_schedule | on_load | When to start the simulation. on_schedule is roadmap and fails fast at apply time. |
Architecture
The generator resolves every field's value (from set, templates, defaults, or the spec's own domain, in that precedence) and hands them to the pipeline as data: the same iso8583.field.N shape codec_iso8583's decode direction produces. Packing those fields into wire bytes is a codec gear's job, downstream of this one, not the generator's.
When to use
- Load testing a Rack or enrichment pipeline at realistic throughput
- Developing against a spec before the counterparty exists
- Demonstrating a deployment without a live scheme connection
- Reproducing a specific traffic pattern deterministically
Configuration
The full field list, with types and defaults, is in the Reference table above. In brief:
- name: traffic
type: sim_source
config:
spec: iso8583-v87-ascii:v2.2.0 # store reference (a file path is roadmap)
seed: 20260904 # mandatory, deterministic runs
trigger: on_load # on_load | on_control (on_schedule is roadmap)
schedule: "2026-01-15T09:00:00Z" # reserved for on_schedule; unused today
rate:
shape: constant # constant | ramp (poisson and spike are roadmap)
tps: 200
duration: 60s
from: 10 # for ramp
to: 400
over: 5m
mix: # overrides spec's simulation.mix
- { use: "0100", weight: 85 }
- { use: "0200", weight: 10 }
- { use: "0800", weight: 5 }
defaults: # overrides spec's simulation.defaults
7: "$NOW" # current time per field format (MMDDhhmmss for datetime)
11: "$STAN"
templates: # overrides spec's simulation.templates
"0100":
2: "$PAN(4111, 16)"
4: "$RAND(100, 5000)"
set: # highest precedence, pins fields
41: "TERM0001"
expiry_monthsandexpired_percentwork together:expiry_monthssets how far ahead a generated expiry date sits by default, andexpired_percentdraws a share of them already expired instead, from the seed, so a run replays exactly. Use them with a responder rule on DE 14 to exercise the decline for an expired card.timezonepicks the clock$NOWand$RRNrender in:local(default),GMT/UTC, or a zone name such asAmerica/Montevideo. ISO 8583 asks for GMT in DE 7; the zone names are built into the binary, and an unknown one is refused when the scenario is applied.
Value precedence
Fields are resolved in this order (highest first):
set(gear config)templates[MTI](gear config)defaults(gear config)spec.simulation.defaults(spec)spec.simulation.templates[MTI](spec)- Enum-aware synthesis from field's value domain
- Empty string
Both field numbers ("2") and aliases ("card.pan") are accepted as keys in defaults, templates, and set.
Macros
| Macro | Description |
|---|---|
$PAN(prefix, len) | Luhn-valid PAN with given prefix and length |
$STAN | Monotonic STAN counter (6 digits) |
$RRN | Retrieval Reference Number (YYMMDD + 6-digit sequence), the date in the timezone of the gear |
$SEQ(name, start) | Named monotonic sequence |
$RAND(min, max) | Uniform random integer in range |
$UUID | UUID drawn from the seed, like every other macro, so that a run replays exactly (version 4 layout) |
$NOW | Current time, rendered in the layout the spec declares for the field (MMDD, hhmmss, MMDDhhmmss, YYMM), in the timezone of the gear. A field with no layout is rendered by its format.kind: date as MMDD, time as HHMMSS, and datetime (or any other kind) as MMDDhhmmss |
$ENUM | Random valid value from field's value set |
$INVALID | Value outside field's closed value set (negative testing) |
$AUTH | 6-character alphanumeric auth code |
Rate shapes
| Shape | Behavior |
|---|---|
constant | Fixed TPS (rate.tps) |
ramp | Linear ramp from from to to over over duration |
poisson | [Roadmap] Poisson process at rate.tps average. Refused today. |
spike | [Roadmap] Bursts at rate.tps with quiet periods. Refused today. |
Runtime control
The gear subscribes to Control Plane commands on subject flux.ctrl.<gear-name>.
Four commands drive it, sent as HTTP on the Mixer and forwarded to the gear
over the bus:
POST /api/v1/control/sim/{action} action: start | stop | rate | reset
The Mixer publishes the command on subject flux.ctrl.<gear-name> with the
gear's name as the address, and waits for the gear to acknowledge receiving
it before answering: the response confirms the command reached a gear, not
that the gear finished acting on it. The body on success is
{"status": "started" | "stopped" | "rate_changed" | "reset"}.
| Command | Args | Effect |
|---|---|---|
sim.start | {"gear": "name", "seed": 123} | Start generation (if trigger=on_control). The seed is optional; when present it re-seeds the generator so the run reproduces exactly. |
sim.stop | {"gear": "name"} | Stop generation. |
sim.rate | {"gear": "name", "tps": 400, "shape": "ramp", "from": 10, "to": 400, "over": "5m"} | Replace the scenario's base rate immediately, without restart. shape is constant or ramp (poisson and spike are roadmap: the gear ignores a command that names them and logs it, changing nothing, and the command is still acknowledged, since acknowledgment confirms delivery, not that the shape was accepted). from, to and over apply to ramp. |
sim.reset | {"gear": "name", "seed": 1234} | Reset generator counters (STAN, RRN, named sequences) and re-seed random generator. Same seed produces identical sequences after reset. |
Errors
| Condition | Response |
|---|---|
| Body that is not valid JSON, or empty | 400 Invalid JSON |
| Missing gear name | 400 gear is required |
| Unknown action | 400 Invalid action |
| No control-plane connection | 503 Control plane not available |
| No gear acknowledged the command (wrong name, or its command queue is full) | 503, naming the gear |
| Publish failure | 500 Failed to publish command |
Example
- name: traffic
type: sim_source
config:
spec: iso8583-v87-ascii:v2.2.0
seed: 20260904
trigger: on_control
rate:
shape: ramp
from: 10
to: 400
over: 5m
duration: 10m
mix:
- { use: "0100", weight: 85 }
- { use: "0200", weight: 10 }
- { use: "0800", weight: 5 }
set:
card.pan: "4111111111111111"
terminal_id: "TERM0001"
When not to use
- To prove interoperability. A generated counterparty shares the codec with the thing under test, so a mutual misunderstanding of the spec passes silently. Use Robot Framework or
iso8583-toolwith an independent implementation for certification; see the tutorial for the fuller reasoning. - As a substitute for a captured production sample.
mix,rateand the spec's own value domains shape the traffic; they do not reproduce the bursts, correlations, or edge cases a real feed accumulates over time. Usesim_sourceto prove a scenario behaves under a shape you chose, not to prove it survives shapes you did not. - To claim broad input coverage from one run. A seed makes a run reproducible, not exhaustive: it walks one deterministic path through the value space. Vary the seed, or use
$INVALIDdeliberately, to reach cases a single run would never generate on its own.
Rationale & extended info
Why the gear does not pack its own bytes
sim_source is manifest-categorized as a logic gear, the same category as sim_responder, bento, and coatcheck. That category boundary is what decides who may touch the wire format, not what would be most convenient for one gear to implement: a logic gear resolves and transforms data, a codec gear packs and unpacks it, and an io gear moves bytes over a socket. sim_source synthesizing a message already means resolving every field's value against the spec, which makes packing it too look like a cheap next step, but taking that step would blur a boundary every other logic gear in this catalog respects. codec_iso8583 packs it instead, exactly as it would for a real counterparty's traffic, so a scenario that swaps sim_source for a real feed changes nothing downstream.
Why the seed is not optional
Every macro is deterministic under the seed, so a run that fails can be replayed exactly. A generator whose output cannot be reproduced turns an intermittent failure into an unfalsifiable story, which is why seed is required rather than defaulting to a random value.
License
The gear is licensed under the Business Source License 1.1.