monoblokand signal conditioning

Stop repeating stream clean-up in every subscriber.

The pattern you already have

Somewhere in your system, a publisher emits a stream that is faster and noisier than anything downstream actually wants.

A car publishes engine RPM ten times a second. A market data feed ticks on every quote. A temperature sensor posts a fresh reading every two seconds, mostly identical to the last one. An OBD2 dongle, a fleet tracker, and an industrial PLC all have the same shape. The publisher emits raw data at its own rate because it does not know, and should not have to know, what each subscriber cares about.

So the subscribers cope. Open the source of three consumers of that stream and you will often find the same defensive code written three times. Parse the frame. Round the float to the precision this consumer needs. Drop the reading if it is the same as the last one. Ignore values inside a deadband. Keep a small window for a moving average. Watch for a jump and raise an alert. None of it is hard. All of it is duplicated.

This is so normal that it can stop looking like a problem. It gets called consumer code, but it is still repeated work.

Why doing it many times is worse than it looks

Every subscriber reads the whole stream, including the messages it is about to throw away, then does the throwing away locally. If forty consumers each subscribe to a stream where ninety per cent of messages are repeated values, you pay to deliver and parse that ninety per cent forty times. The network moved it, every consumer socket received it, every consumer CPU parsed it, and then every consumer dropped it.

Then there is drift. The rounding logic in consumer A is 2dp. In consumer B, written eight months later by someone who has since left, it is 3dp. The deadband in one is 0.5 and in another is 0.6 because it was tuned once for a demo and never changed back. The dedupe in a third has an off-by-one error in the window.

Each difference is small. Together, they mean the same data is no longer the same data, depending on which consumer you ask. Nobody set out to create that problem. It grew one copy at a time.

The rules describe the data, not the consumer

Take a rule like this: an RPM stream should be rounded to whole numbers and should only emit a new value if it moved by at least 50. That is not a fact about one consumer. It is a fact about the stream. It is true no matter who is listening.

The same applies to duplicate reprints, price jumps, sensor deadbands, rolling averages, and threshold alerts. If the rule is true for the stream, putting it in every consumer is the wrong place for it. You have taken one definition and scattered it across many processes.

The conditioning should live in one upstream place, defined once.

A broker or rules-driven consumer is a reasonable place

Every consumer already meets the stream at the broker, and any one shared process can subscribe there before the data fans out. That shared process is the right place to run the rules.

Condition the stream once, and every subscriber downstream receives the same cleaned data. Subscribers no longer parse values they will discard because the discarding has already happened. They no longer carry rounding logic because the numbers arrive rounded. They no longer drift because they share one definition.

There is another useful effect. Once conditioning happens in one shared place, each result can have its own subject. The rounded and deduplicated price stream is a subject. The value crossed the threshold event is a subject. A consumer that only wants stable prices subscribes to the stable subject and never receives the raw stream.

That is the core idea. Signal conditioning, including rounding, deadbands, squelch, windows, moving averages, OHLC bars, and derived alerts, belongs in one upstream rules-driven step as a property of the stream.

monoblok is a NATS-native version of this approach

monoblok speaks the NATS protocol, so it can sit in front of existing publishers, subscribe to subjects already in a NATS cluster, or run standalone as a lightweight broker.

High-volume data source flowing through monoblok before clean messages are bridged to a NATS cluster
monoblok can condition noisy messages before forwarding only the useful subjects to a NATS cluster.

You define the conditioning in small patchbay config files. Rules say what to round, what to deduplicate, what to aggregate, and what to alert on. monoblok republishes clean derived subjects, and the rules stay in one place.

monoblok animation showing rounded and squelched values published as clean NATS subjects
Rounding and squelch happen once before subscribers receive the derived subjects.

A worked example: a market data feed delivers JSON frames with many fields per message. Three rules condition the whole feed. One splits each frame into per-field subjects. One rounds the price, drops duplicate reprints, and mirrors the result to a .stable subject. One watches for a jump between consecutive ticks and emits an alert. A consumer that only cares about clean prices subscribes to .stable and receives a per-symbol stream of rounded floats, with no JSON parsing, dedupe code, or raw stream.

The full pattern, including the rules, is in the market data write-up.

monoblok is written in C, is MIT licensed, and can sustain millions of messages per second. The benchmark results in the repository show the method and the per-host numbers. For ESP32 telemetry, tinyblok runs the same patchbay model in firmware, so a sensor node can condition its own output before it crosses the network.

See it in 10 seconds

There is a public, no-auth demo server at nats://demo.monoblok.host:4222. The homepage has a live demo for publishing sample values and watching monoblok emit derived subjects such as .stable, .delta, .smoothed, .avg5s, and .overload.

When the idea is clear, read the overview or check the install options.

© 2026 Alex Reid / Lexvica Limited. monoblok and tinyblok are MIT licensed and supported by the author.
Commercial engagements are also available. alex@lexvica.com