Skip to main content

Operating fluxrig

How the two processes are started, how a Rack is brought into a fleet, what changing behaviour looks like, what normal looks like, and what the system does on its own before anyone looks.

What you are running

Two processes, with different jobs and different failure consequences.

RoleIf it stops
Rack (fluxrig)Runs a scenario: receives, transforms and forwards trafficTraffic on that node stops
Mixer (fluxrig-mixer)Enrolls Racks, deploys scenarios, collects telemetryTraffic keeps flowing; nothing new can be deployed

A Rack connects outbound to the Mixer over mTLS. The Mixer never dials a Rack, so a Rack behind NAT or inside an isolated segment needs no inbound rule.

Starting the Mixer

fluxrig-mixer -c fluxrig-mixer.toml # config file
fluxrig-mixer -c fluxrig-mixer.toml -s flow.yaml # and a scenario to load at boot

It listens on two ports: the API on 8090 and the bus on 4222. The bus is embedded, not a dependency: the Mixer runs its own NATS server (the Snake), so there is no broker to deploy beside it.

That is a constraint as well as a convenience. The bus cannot be replaced by an existing NATS deployment: the Mixer starts its own on every boot, and there is no configuration that points it at another. What is configurable is where it listens (snake.port) and what address Racks are given for it (snake.url), so the bus can move host and port but remains the Mixer's.

On first start it writes its own identity and creates what it needs. Under the store directory (./data by default):

FileWhat it is
cluster.keyThe Ed25519 authority that signs Rack passports and Wasm payloads
mixer.fluxThe Mixer's own passport, signed by that key
flux.duckdbThe registry and telemetry

cluster.key is the one to back up. The Mixer generates it when it is missing, and then fails to verify the mixer.flux beside it, so a Mixer that loses its key and keeps its data refuses to start. Every passport it ever issued was signed by that key.

Bringing a Rack into the fleet

A Rack enrolls itself on first start. It sends a hello, the Mixer records it as pending, and it stays there until somebody says yes:

fluxrig admin racks list # who is asking, and who is already in
fluxrig admin racks approve <id> # let one in

Approval is a deliberate step: an enrolled Rack receives scenarios, so adopting one is granting it work. A Mixer can be told to adopt automatically with --auto-adopt, which is for a development machine and says so.

Once approved, the Rack holds a signed passport (rack.flux in its data directory) and its own identity. Suspended Racks come back with fluxrig admin racks activate <id>; fluxrig admin racks remove <id> takes one out of the registry for good.

Changing what runs

Behaviour is a scenario, and a scenario is a versioned artefact rather than a file someone edits in place. The change is filed, then deployed.

fluxrig scenario diff payment_flow.yaml # against what is running now
fluxrig scenario import payment_flow.yaml \
--name payment-flow --tag v1.2.0 --dry-run # validate, change nothing
fluxrig scenario import payment_flow.yaml \
--name payment-flow --tag v1.2.0 --api # file it and send it to the Mixer
fluxrig topology list # what is deployed where

A protocol spec travels the same way, and the two are related: a scenario names the specs it needs, and deploying it carries them.

fluxrig spec import iso8583-v87-ascii.yaml
fluxrig spec list # what the store holds
fluxrig spec history iso8583-v87-ascii # every version of one spec

Both live in the content-addressable store, so a version is immutable: the same name and tag always resolve to the same bytes. A URN is resolved when a gear starts, not per message, so importing a newer version changes nothing until the scenario restarts.

What normal looks like

fluxrig topology status # what the Mixer believes about the fleet
fluxrig metrics # recent telemetry
curl -s localhost:8090/api/v1/health

Two numbers are worth watching per gear, because together they say whether work is going in and coming out: flux.gear.messages_in and flux.gear.messages_out. A gear whose messages_in climbs while messages_out does not is dropping or erroring, and that comparison is faster than reading any log.

Beside them, flux.gear.errors and flux.gear.processing_time_ms on every gear. I/O gears add flux.port.bytes_in, flux.port.bytes_out, flux.port.messages_in, flux.port.messages_out, flux.port.connections_active and flux.port.connections_total. The bus reports flux.bus.publish_count and flux.bus.publish_errors. The ISO 8583 codec adds flux.codec.iso8583.fields_count and, when validation is on, flux.iso8583.violations.

Telemetry lands in DuckDB on the Mixer and is flushed to Parquet. See Telemetry and analytics.

A Rack keeps running without the Mixer

This is a design decision, not a fallback. A Rack that has enrolled resumes its last scenario from local state, and if the bus is unreachable at startup it logs Starting in OFFLINE Mode and carries on processing.

The consequence for an operator: a silent Mixer does not stop money moving, and a Rack that looks absent from topology status may be serving traffic normally. Check the Rack before declaring an outage.

A Rack that has never enrolled and cannot reach the bus refuses to start, because it has no identity and no scenario to resume.

What degrades, and what fails

SituationWhat happens
Mixer unreachableRacks keep processing. Telemetry queues locally; no scenario changes land.
A gear returns an errorFollows that gear's on_error: reject (out the error port), drop (discard), kill (fail the gear).
A destination stops answeringThe Conductor's ticket expires and surfaces on the error port. See Conductor.
A message breaks a spec ruleDepends on the codec's validation: off, warn (logged and counted) or enforce (fails the message, then on_error).
An enrichment call exceeds its deadlineThe pipeline continues with the fact absent, rather than holding the transaction. See the roaming tutorial.

When something is wrong

Decide which half first. A Rack and a Mixer fail independently, and the answer changes what you look at next.

fluxrig check # bus, Mixer API and local storage, from this node
curl -s localhost:8090/api/v1/health

If check passes and traffic is still wrong, the fault is in the scenario or a gear, not in the plumbing.

A Mixer that will not start is usually saying so: it verifies mixer.flux against cluster.key before anything else, and refuses rather than issuing identities it cannot stand behind.

Logs. fluxrig logs queries the Mixer for a fleet view; fluxrig tail <node> follows one Rack live; fluxrig inspect-logs reads the binary WAL on the Rack itself, which is the only one that works when the Mixer does not.

Which spec ran. Every message the ISO 8583 codec handles carries codec.spec_hash, codec.spec_id and codec.spec_version. When two Racks behave differently on what looks like the same traffic, compare the hashes: they are running the same spec or they are not, and nothing else answers that question.

Configuration

Ports, timeouts, store locations and TLS are in the configuration reference. Two rules hold throughout: every wait has a timeout, and every timeout is a configuration field with a documented default.