Skip to main content

sim_responder

Commercial gear, licensed separately from the engine. The engine, the SDK and every other gear in this catalog are Apache 2.0. sim_responder 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_responder/, a separate repository from the engine. See the enterprise introduction and the worked tutorial.

The sim_responder gear acts as an ISO8583 authorizer simulator. It is a pure logic gear: a decoded request arrives on the in port, the responder applies defaults and rules, and a fields-only response leaves on out for the downstream codec gear to pack. It never touches the wire: no listener, no framing, no packing. Transport (TCP/mTLS, framing, per-connection reply routing) is the io_iso8583 gear's job; bytes are the codec_iso8583 gear's job.

AttributeDetails
Source Codepkg/gears/sim_responder
Pairs Withcodec_iso8583 on both sides (decode the request, encode the response), typically fed by io_iso8583 in server mode
Port INDecoded ISO8583 request
Port IN CardinalitySingle
Port OUTFields-only ISO8583 response
Port OUT CardinalitySingle
Always Emitted Metadataiso8583.mti, iso8583.mti_class, sim.response_to, sim.correlation_id
Conditionally Emitted Metadataconn.id (propagated from the request, when present)
Mandatory Consumed Metadataiso8583.mti (to resolve the response MTI)
Optional Consumed Metadataconn.id (so the reply routes home)
Signals SentNone
Signals Subscribedsim.start, sim.stop, sim.reset (Control Plane; sim.rate is acknowledged but ignored)

Typical topology (authorizer simulator reachable over TCP):

io_iso8583 (server :10002) → codec (decode) → sim_responder → codec (encode) → io_iso8583

Replies return on the originating connection: the responder propagates the conn.id transport metadata, which the io gear's strict_connection_routing uses to route the reply home.

Reference​

The identity, ports, and configuration below are generated from the gear's manifest, so they stay in lockstep with the code.

Typesim_responder
Categorylogic
Statusstable
Terminustransparent

Responds to ISO8583 requests as an authorizer simulator with configurable rules and latency.

Ports

PortDirectionRoleSummary
ininputrequestIncoming ISO8583 request from the scheme.
outoutputresponseOutgoing ISO8583 response to the scheme.

Configuration

FieldTypeRequiredDefaultDescription
specstringyes-Spec reference (store URN or file path). Example: iso8583-v87-ascii:v2.2.0
defaultobject-Default response field values. Keys can be field numbers ("39") or aliases ("resp_code").
delaystring50msBase latency to simulate scheme processing time.
rulesarray-Conditional response rules, evaluated in order. Each has name, when, set, and an optional per-rule delay.
timezonestringlocalThe clock that $NOW and $RRN render: local, GMT (or UTC), or a zone name such as America/Montevideo.
triggerenum: on_load, on_controlon_loadon_load processes immediately; on_control drops requests until sim.start arrives.

Architecture​

When to use​

  • Simulating an issuer authorizer for load testing or development
  • Testing timeout, retry, and correlation logic under realistic latency
  • Reproducing specific failure modes (insufficient funds, expired card, etc.)
  • Demonstrating a deployment without a live issuer connection

Configuration​

The full field list, with types and defaults, is in the Reference table above. In brief:

- name: authorizer
type: sim_responder
config:
spec: iso8583-v87-ascii:v2.2.0 # store reference (a file path is roadmap)
trigger: on_load # on_load | on_control
delay: 50ms # base latency
default: # default response fields
39: "00" # approved
38: "$AUTH" # auth code macro
rules: # conditional overrides
- name: Insufficient funds over limit
when: field(4) > 1000000
set:
39: "51"
delay: 10ms # optional per-rule delay override
- name: Expired card
when: field(14) < 2501
set:
39: "54"
delay: 5ms

Rules​

Each rule has:

KeyTypeRequiredDescription
namestringyesRule identifier for logging
whenstringyeswhen expression evaluated against the request
setmap[string]stringyesResponse field overrides when rule matches
delaystringnoPer-rule delay override (replaces base delay)

Rules are evaluated in order. The first matching rule wins and its set overrides any previous values. If a rule has a delay, it replaces the base delay for that message (it does not add to it).

A configuration that cannot work is refused when the scenario is applied, instead of answering wrongly: a delay that is not a duration, a rule without a name, a when or a set, a when that does not parse, a key that is neither a field number nor an alias of the spec (a subfield alias is refused too: set the whole field), a macro that does not exist, and a $RAND or $SEQ argument that is not a number.

When expressions​

Rules use the same when expression language as the spec's validation rules:

  • Field access: field(4) or field(14)
  • Presence check: present(2)
  • MTI access: mti
  • Comparisons: ==, !=, <, >, <=, >=
  • Logic: &&, ||, !
  • Parentheses for grouping

Amount fields compare numerically, date/time fields compare chronologically, and codes compare as strings.

String literals use single quotes, as in field(48) == 'RS01'. A double-quoted string does not parse, and the scenario is refused when it is applied.

Response MTI​

The response MTI is derived from the spec's message catalog: pairs_with of the request MTI. A 0100 request gets a 0110 response because the catalog declares they pair.

Echo semantics​

Fields with response_value: echo or response_value: modified in the spec are copied from the request to the response automatically. This includes STAN (DE 11), PAN (DE 2), Amount (DE 4), Terminal ID (DE 41), etc., per the spec's per-MTI rules.

Macros​

MacroDescription
$AUTH6-character alphanumeric auth code
$RRNRetrieval Reference Number (YYMMDD + 6-digit sequence), the date in the timezone of the gear
$STANEchoes STAN from request (DE 11)
$SEQ(name, start)Named monotonic sequence
$RAND(min, max)Uniform random integer in range
$UUIDUUID v7
$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

Delay semantics​

The delay in config is the base latency. A matching rule's delay replaces the base delay for that message (it does not add to it). This allows a rule to specify a shorter or longer latency than the base.

delay: 100ms
rules:
- name: Fast approval
when: field(4) < 10000
delay: 1ms # uses 1ms, not 101ms
- name: Slow decline
when: field(4) > 1000000
delay: 500ms # uses 500ms

A paused gear drops requests with a debug log rather than blocking: stalling would stall the whole pipeline. The configured delay sleeps inside request processing, and each wire into the gear delivers its messages one at a time, so the throughput of one wire is bounded by 1/delay: 20 requests per second at the default 50ms. That is the simulated issuer latency, not a bug.

Runtime control​

The gear subscribes to Control Plane commands on 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"}Resume processing (clears on_control pause / sim.stop).
sim.stop{"gear": "name"}Pause processing; requests are dropped, not queued.
sim.reset{"gear": "name"}Clear macro sequence counters ($SEQ, $RRN).
sim.ratenoneIgnored: the responder has no traffic generator, but the command is still acknowledged, since acknowledgment confirms delivery, not that the rate changed.

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: authorizer
type: sim_responder
config:
spec: iso8583-v87-ascii:v2.2.0
trigger: on_load
delay: 50ms
default:
39: "00"
38: "$AUTH"
rules:
- name: Insufficient funds
when: field(4) > 1000000
set:
39: "51"
- name: Expired card
when: field(14) < 2501
set:
39: "54"
- name: Fraud suspect
when: field(48) == 'RS01'
set:
39: "59"
44: "FRAUD"
delay: 200ms

When not to use​

Do not use sim_responder 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.

Rationale & extended info​

Why a pure logic gear​

The responder never opens a socket, frames a byte, or packs a message. Keeping transport (io_iso8583), codec (codec_iso8583), and response logic (sim_responder) as three separate gears means any of the three can be swapped without touching the others: a real authorizer's logic could sit behind the same io_iso8583/codec_iso8583 pair this simulator uses today, and the simulator can be pointed at a different transport without changing a rule.

Why delay is a sleep, not a rate limit​

The base delay and each rule's override model the issuer's own processing time, not a throughput cap. Because each wire delivers its messages one at a time, the delay does bound one wire's throughput as a side effect (1/delay), which is what makes it useful for exercising a caller's timeout path deliberately, rather than as an accident of implementation.

License​

The gear is licensed under the Business Source License 1.1.