Skip to main content

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_source is 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 in fluxrig-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.

AttributeDetails
Source Codepkg/gears/sim_source
Pairs Withcodec_iso8583 (encode), typically followed by io_tcp or io_iso8583 to reach the wire
Port OUTFields-only ISO8583 message
Port OUT CardinalitySingle
Always Emitted Metadataiso8583.mti, sim.seed
Conditionally Emitted MetadataNone
Mandatory Consumed MetadataNone (source gear: no input port)
Optional Consumed MetadataNone
Signals SentNone
Signals Subscribedsim.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.

Typesim_source
Categorylogic
Statusstable
Terminusio

Generates ISO8583 traffic from a spec's simulation section with deterministic macros and configurable rate shaping.

Ports

PortDirectionRoleSummary
outoutputegressGenerated ISO8583 messages emitted into the pipeline.

Configuration

FieldTypeRequiredDefaultDescription
seedintegeryes-Random seed for deterministic generation. Mandatory for reproducibility.
specstringyes-Spec reference (store URN or file path). Example: iso8583-v87-ascii:v2.2.0
defaultsobject-Field defaults. Keys can be field numbers ("2") or aliases ("card.pan").
expired_percentinteger0The share of generated expiry dates, from 0 to 100, that are already expired: between one and expiry_months months before the current one.
expiry_monthsinteger24How far ahead an expiry date (a field whose layout is YYMM, DE 14) is generated, in months from the current one.
mixarray-Traffic mix: a list of {use, weight} entries overriding the spec's simulation.mix.
rateobject-Rate shape: shape (constant, ramp, poisson [Roadmap], spike [Roadmap]), tps, from, to, over, duration.
schedulestring-Reserved for the on_schedule trigger; parsed but unused today.
setobject-Pinned field values (highest precedence). Keys can be field numbers or aliases.
templatesobject-Per-MTI field templates. Map: MTI -> (field -> macro/literal).
timezonestringlocalThe clock that $NOW and $RRN render: local, GMT (or UTC), or a zone name such as America/Montevideo.
triggerenum: on_load, on_control, on_scheduleon_loadWhen 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_months and expired_percent work together: expiry_months sets how far ahead a generated expiry date sits by default, and expired_percent draws 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.
  • timezone picks the clock $NOW and $RRN render in: local (default), GMT/UTC, or a zone name such as America/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):

  1. set (gear config)
  2. templates[MTI] (gear config)
  3. defaults (gear config)
  4. spec.simulation.defaults (spec)
  5. spec.simulation.templates[MTI] (spec)
  6. Enum-aware synthesis from field's value domain
  7. Empty string

Both field numbers ("2") and aliases ("card.pan") are accepted as keys in defaults, templates, and set.

Macros​

MacroDescription
$PAN(prefix, len)Luhn-valid PAN with given prefix and length
$STANMonotonic STAN counter (6 digits)
$RRNRetrieval 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
$UUIDUUID drawn from the seed, like every other macro, so that a run replays exactly (version 4 layout)
$NOWCurrent 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
$ENUMRandom valid value from field's value set
$INVALIDValue outside field's closed value set (negative testing)
$AUTH6-character alphanumeric auth code

Rate shapes​

ShapeBehavior
constantFixed TPS (rate.tps)
rampLinear 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"}.

CommandArgsEffect
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

ConditionResponse
Body that is not valid JSON, or empty400 Invalid JSON
Missing gear name400 gear is required
Unknown action400 Invalid action
No control-plane connection503 Control plane not available
No gear acknowledged the command (wrong name, or its command queue is full)503, naming the gear
Publish failure500 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-tool with an independent implementation for certification; see the tutorial for the fuller reasoning.
  • As a substitute for a captured production sample. mix, rate and 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. Use sim_source to 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 $INVALID deliberately, 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.