Concluded·3 Aug 2026 · 5 min read

A Unified Namespace built from parts, to find out which part is the product

Mosquitto 2 · MQTT · JSON Schema (Ajv) · Node.js 20 · Docker Compose · Proxmox LXC

The common view is that a Unified Namespace is an MQTT broker with a sensible topic structure, and that the products sold around it are therefore optional. I mostly believed that, so I built one from primitives.

Building it was easy. Finding out what was on it was not.

The two agreements

A namespace is two conventions and nothing else.

The address. northwind/<site>/<area>/<line>/<cell>/<tag> — ISA-95 shaped, so a topic states where in the plant a reading comes from.

The payload. Every message must carry timestamp_ms, value, unit and a quality of good, uncertain or bad.

THE TWO AGREEMENTS THAT MAKE A NAMESPACE

northwind
enterprise
/
rotterdam
site
/
utilities
area
/
line-01
line
/
filler
cell
/
pressure
tag
AND THE PAYLOAD MUST CARRY
timestamp_msvalueunitquality: good | uncertain | bad
The address carries the hierarchy and the payload carries the meaning. Both are just agreements — nothing in a broker enforces either of them.

Both live in sim/contract.js, thirty-odd lines including the topic regex. That file is the entire difference between a namespace and a broker with tidy names — provided something enforces it.

The estate

Sitesrotterdam, houston, seville
Structure3 sites × 2 areas × 2 lines × 2 cells × 6 tags = 144 tags
Cadence48 fast (1 s) · 48 slow (30 s) · 48 event-driven (~10 min)
Run186 s, 9,120 messages
Broker memory2.36 MiB

The cadence mix is the load-bearing design choice. A generator where every tag publishes at the same rate would have made this lab produce a completely different and much less true answer.

Experiment 1 — discovery by listening

A consumer subscribed to northwind/# and tried to rebuild the tag tree from traffic, which is the only mechanism a bare broker offers.

Listening forTags foundof 144
10 s4833.3%
30 s4833.3%
60 s9566.0%
120 s9666.7%
180 s9666.7%

Discovery time for the ones that did appear: p50 33 s, p95 33 s, max 63 s.

TAGS DISCOVERED BY LISTENING

04896144144 exist96 found10s60s120s180stime spent listening
The curve stops climbing at ninety-six. The remaining forty-eight tags exist, are correctly configured, and simply never published during the run.

Forty-eight tags were never seen. Correctly configured, correctly named, on the right topics — and silent, because they are the event-driven ones: filter_blocked, estop_tripped.

WHAT A LISTENING CONSUMER KNEW ABOUT

96 SEEN — the ones that talk

Pressure, flow, temperatures, run hours. Anything on a scan class shows up within a minute simply by publishing.

48 NEVER SEEN — the ones that matter

filter_blocked, estop_tripped. Configured, correct, on the naming convention, and completely invisible until the day they fire.

The invisible third is not a random sample. It is precisely the tags that only speak when something has gone wrong.

A broker holds no directory. A tag exists, as far as any consumer can tell, only once it has published. Every "browse the namespace" screen you have used is something built on top that keeps its own catalogue.

The consequence is not a slightly incomplete picture, it is a biased one. The tags you cannot discover are precisely the rare, important ones. A dashboard assembled by discovery shows every pressure in the plant and no emergency stops, and looks complete while doing it.

Nor can listening distinguish a tag that is quiet from a tag that does not exist, or from one that died last Tuesday — which is the same staleness-versus-liveness problem that showed up in the UMH lab from the other direction.

Experiment 2 — what enforces the contract

About 2.6% of publishes carried a defect, chosen to be the kind that happens on real projects rather than obviously silly ones.

DefectCount
missing unit65
bare number, no envelope50
quality: "OK" instead of good48
timestamp in seconds, not milliseconds44
topic with capitals and a space32
total239
Result
Accepted by the broker239 of 239 (100%)
Rejected by the validator239 of 239 (100%)

239 DELIBERATELY BROKEN MESSAGES

THROUGH THE BROKERbroker239 throughTHROUGH A VALIDATORcontract239 stopped
missing unit65
bare number, no envelope50
quality "OK" not "good"48
seconds instead of milliseconds44
capitals and a space in the topic32
The broker delivered every malformed message faithfully and instantly. It has no opinion about your contract, and it never will.

The broker delivered all of it, faithfully and instantly. It has no opinion about the contract, which is correct behaviour for a broker and a serious problem for anyone who assumed the namespace was protecting them.

The validator caught everything — but only because it exists. Forty lines of Ajv against the schema. Delete them and the malformed data flows on, looking exactly like good data.

The timestamp defect deserves attention: forty-four readings arrived stamped in 1970. Nothing rejected them. In a historian they either vanish into an ancient partition or silently skew every aggregate that touches them, and the discovery lag on that kind of fault is measured in months.

What could and could not be built

PartCostResult
a brokerminutes✓ 2.36 MiB for 144 tags
an ISA-95 naming conventionfree✓ it is a document
a payload contract~30 lines✓ JSON Schema
enforcement of it~40 lines✓ 239/239 caught
a directory of what existsnot possible by listening✕ 33% never found

BUILDING ONE FROM PARTS

a brokerminutes2.36 MiB carrying 144 tags
an ISA-95 naming conventionfreeit is a document
a payload contract~30 linesJSON Schema
enforcing that contract~40 linescaught 239 of 239
a directory of what existsimpossible by listeninga third of the plant stayed invisible

Which is the honest answer to buy-versus-build. The broker is the cheap part. The directory is the product.

Four of the five parts took an afternoon. The fifth cannot be obtained from a broker at any effort, because the information is not in there.

THE FOUR NUMBERS

2.36 MiB
THE WHOLE BROKER, 144 TAGS
66.7%
OF THE PLANT DISCOVERABLE
239/239
BAD MESSAGES THE BROKER PASSED
0
OF THEM IT OBJECTED TO
The first number is why people build their own. The other three are why they end up buying something.

Verdict

The broker is the cheap part. At 2.36 MiB it is close to free, and four of the five components of a working namespace are an afternoon of unremarkable code.

The fifth cannot be obtained from a broker at any effort, because the information is not in there to be had. Something must hold a catalogue of what exists, what it means, what it should be publishing and whether it has gone quiet. That catalogue has to be maintained as the plant changes, and maintaining it is the actual job.

So the buy-versus-build question is not "can I run Mosquitto instead of a product". Obviously you can. It is whether you want to own a catalogue and a validator and keep them true for a decade.

Three sites, a stable tag list and an engineer who enjoys this: build it, and you will understand your own data better for having done so. Forty sites and a new one every quarter: the catalogue is the product, and that is what you are buying.

The thing nobody should do is build the first four parts, skip the fifth, and call the result a Unified Namespace.

The argument, without the measurements, is in the article.

Newsletter

New essays, by email.

SCADA, cloud, AI, and the plant floor — a short email when something new is published. No noise, unsubscribe anytime.