Coat Check gear
The coatcheck gear functions as the Asymmetric Bus Driver of the fluxrig mixer. It implements a high-speed context-correlation engine. The engine lets the system remain stateless at the edge. It preserves complex transaction context (session IDs, tracing headers, and correlation metadata) across asynchronous boundaries.
fluxrig resolves the Asymmetric Bus Scaling problem by "checking in" session state to a distributed cache. This enables horizontal scalability. Requests can exit via one Rack. Responses can return via another.
NOTE
Parking, not tokenization. The Coat Check parks a value and restores the same value on the reply. It does not substitute a surrogate: tokenization (replacing a PAN with a vault-backed surrogate) is a separate gear on the roadmap. For a payment switch, the PAN must reach the scheme to authorize, so it is not parked on the primary path.
NOTE
The Conductor gear generalizes the Coat Check pattern. It keeps this correlation engine as a "valet" ticket store. It adds connection routing and reply matching, with a pluggable, local-by-default ticket store. Both gears ship. Reach for the Conductor when you need switching. Stay with the Coat Check when single-leg field parking is enough. It also runs in the lean -tags nobento Rack.
| Attribute | Details |
|---|---|
| Analogy | State Cache / Patch Memory |
| Source Code | pkg/gears/native/coatcheck |
| Tech Stack | NATS JetStream (KV) |
| Pattern | Claim Check EIP |
| Mandatory Consumed Metadata | [key_fields] (The Ticket) |
| Optional Consumed Metadata | meta.coatcheck.ttl |
| Signals Sent | flux.event.timeout (Metadata type) |
Reference
fluxrig gears doc coatcheck generates the identity, ports, and configuration below from the gear's manifest, so they stay in lockstep with the code.
| Type | coatcheck |
| Category | logic |
| Status | stable |
| Terminus | opaque |
Sessionless context correlation: parks fields under a key on store, restores them on the matching reply.
Ports
| Port | Direction | Role | Summary |
|---|---|---|---|
in | input | message | Message to check in (store) or check out (restore). |
out | output | message | Forwarded message. |
error | output | error | Restore misses under the configured on_missing policy. |
Configuration
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
bucket | string | yes | - | NATS KV bucket that holds the parked context. |
mode | enum: store, restore, daemon | yes | - | store parks fields under the key; restore reattaches them on the matching reply; daemon governs a bucket's TTL. |
await_store | boolean | true | store mode: whether the message waits for the entry to be written. True when the reply depends on it (a stripped field that must be reattached). False when the entry only enriches a record, so a slow control plane cannot hold or fail the message; a failed write is logged and the entry is lost. | |
default_ttl | string | 1m | how long a parked entry lives before expiry, e.g. '30s'. | |
include_values | boolean | false | daemon mode: include the expired entry's data (expired_ctx) in the timeout event this daemon emits. A per-message TTL override (meta.coatcheck.ttl) is honored either way. | |
key_fields | array | - | message fields whose values form the correlation key. | |
key_normalize | enum: trim, numeric, none | trim | how key field values are canonicalized before joining: trim removes surrounding whitespace; numeric also drops leading zeros so fixed-width fields agree across differing specs; none joins them exactly as rendered. | |
max_ttl | string | 5m | daemon mode: safety cap on any per-message TTL override. | |
merge_strategy | string | - | restore mode: overwrite replaces existing metadata; any other value preserves it. | |
on_missing | enum: error, drop, forward | error | restore mode: what to do when no entry is found (expired/never stored): error, drop, or forward without context. Forwarding a reply with its stripped fields unrestored must be requested by name. | |
replicas | integer | - | daemon mode: KV bucket replica count. | |
storage | enum: file, memory | - | bucket backing store: file (durable) or memory. | |
store_timeout | string | 5s | store mode with await_store=false: how long the detached write may take before it is abandoned. | |
value_fields | array | - | store mode: fields (or meta.*) to park under the key and restore later. |
A bucket needs an owner
A store and a restore are not enough on their own. No component creates the bucket they use on demand. Expiring entries needs a single owner, not every Rack sweeping the same keys. A third instance in daemon mode declares the bucket and governs its TTL.
Deploy it once per bucket. Without it the first store fails with bucket not found.
- name: "in-flight-daemon"
type: "coatcheck"
config:
mode: "daemon"
bucket: "auth_in_flight"
storage: "memory"
replicas: 1
default_ttl: "30s"
Seen in use
Enriching an authorization with a network signal uses a store/restore pair to measure an authorizer's round trip. It covers the two things that fail quietly there. First, both sides have to render the key the same way (key_normalize). Second, the restore has to overwrite rather than preserve. Otherwise the reply routes back into the wrong connection.
Architectural signal path
In fluxrig, the "Coat Check" is a virtual state room shared across the mesh. This gear always backs its tickets with a NATS KV bucket. That shared backing enables the Asymmetric Bus: any Rack can check out a ticket another Rack checked in. The Conductor generalizes this with a pluggable, local-by-default store. It uses the Message Flow Architecture to decouple identity from transport.
NOTE
External endpoints never connect to the Coat Check directly. Requests and responses enter the pipeline through I/O gears, which bridge each bidirectional socket onto unidirectional wires. The Coat Check only ever sees one-way fluxMsg traffic.
Operational modes
The gear behaves as a specialized processor depending on its configured mode:
Mode: store (Check-In)
Role: Saves context before sending a message to a stateless transport.
- Extracts
key_fields(Correlation Key) andvalue_fields(Context Blob). - Key Generation: The gear joins the extracted key fields and encodes them using URL-Safe Base64 (
base64.RawURLEncoding). - Saves the blob to the persistent NATS KV
bucketwith attl. - Forwards the original message unmodified to the
outport.
Mode: restore (Check-Out)
Role: Re-attaches context to a response coming back from a stateless transport.
- Extracts
key_fieldsfrom the response. - Lookups the Context Blob in the KV Store.
- Found: Merges context into message metadata and forwards to
out. - Missing: Applies
on_missinglogic (error/drop/forward).
Mode: daemon (Governance)
Role: Bucket Governance & Timeout Detection. The Mixer manages this Singleton virtual gear. Only one instance runs globally per bucket to ensure no split-brain sweeping for expired TTLs.
- Governance: Responsible for creating and configuring the bucket (storage type, replicas).
- Monitoring: Detects expired keys and emits
flux.event.timeout.{bucket}events to the Control Plane.
Configuration notes
The full field list, with types and defaults, is in the Manifest reference table at the top of this page. This gear follows the Air-Gap First philosophy, using embedded NATS for zero-dependency state.
WARNING
The bucket lives on the Mixer, encrypted at rest. A Coat Check bucket is a key-value bucket of the bus. The parked context travels to the Mixer over the bus whatever storage says. With storage: "file" the Mixer writes it to disk and encrypts it there by default (snake.store_encryption), as it does every message of the bus. See data at rest. With storage: "memory" it stays in the memory of the Mixer process and never reaches a disk. Either way the Mixer holds the key or the memory, so it reads the context. A client of the bus can read it too. Parking regulated data (PAN, PIN blocks, track data) is acceptable when the store encryption is on and the Mixer is inside your cardholder data scope:
- Keep
snake.store_encryptionon. - Give the Mixer an operator key file for production.
- Run it hardened (memory locked against swap, core dumps disabled).
A bucket held in the memory of the Rack itself is [Roadmap].
IMPORTANT
Singleton Governance: You must always define one mode: daemon gear for every bucket. The fluxrig runtime will refuse to start if this dependency is missing, as it is critical for reaping expired tickets and avoiding storage leaks.
TIP
TTL Overrides: You can dynamically override the default_ttl per-message by setting the meta.coatcheck.ttl metadata field (e.g., in a logic gear or via mapping) before checking in. The gear caps this override at max_ttl.
Use case: asymmetric scaling (active payment mixer)
In a high-scale financial environment, a Load Balancer (LB) distributes incoming TCP connections across a cluster of Racks. The Asymmetric Bus enables true active/active topologies where a request can exit via Rack A, and the response can arrive at Rack B. Rack B simply "Checks out" the original conn_id from the shared state room. It routes the response back to the originator without any local session state.