Making a Unified Namespace trustworthy — a three-part series: 1 · UNS vs Data Swamp · 2 · Data Contracts · 3 · Asset Models.
Part 2 ended on a gap. Schematic and semantic contracts keep a single message honest — the right fields, the right types, a vocabulary you can't typo your way out of. But a message is always published by something: a machine, a line, a cell. And the question a contract doesn't answer is what that thing is, and what claiming to be that kind of thing obligates it to expose. That's the asset model — the rung above message contracts — and it's where a KPI you can actually trust comes from.
From message shapes to asset types
A semantic contract pins what a single signal's value means — LineState says a line's state is one of five allowed values. It still doesn't say what kind of thing that signal belongs to. The step up is to name the asset type itself as a class — an interface — and list the signals any asset of that type must expose:
name: Machine # the base every connector-observed asset fulfils
version: '1.0'
signals:
- { name: heartbeat, type: bool, required: true }
- { name: is_connected, type: bool, required: true }
---
name: AvailabilityMachine
version: '1.0'
extends: [Machine] # inherits heartbeat + is_connected
signals:
- name: machine_status
type: int
required: true
description: |
Standardized operating state, one definition for the whole fleet.
An integer enum aligned to the PackML state model, e.g.
2 Stopped · 4 Idle · 6 Execute · 9 Aborted
These interfaces are classes: they have a name, a version, and they inherit. A concrete asset then declares which types it is — implements: [AvailabilityMachine, OEEProducer] — exactly the way an object declares the interfaces it satisfies. The message contract from Part 2 is still there underneath; the interface is the contract one level up, attached to the asset rather than to a single message. That's the difference between "we publish a state field" and "this is an availability-bearing machine, and here is what that obligates it to expose."
The schematic part of this is reusable across customers. The asset types almost always grow customer-specific extensions, because what counts as a packaging line — or a CNC cell, or a coating run — is partly universal and partly specific to a given operation. That's expected. The point isn't to make every customer's asset model identical. It's to make sure it exists, in code, on the path of least friction.
Enforcement moves up a level
In Part 2, enforcement kept a message honest: the library does the encoding and validation, so a wrong shape is harder to publish than a right one. The asset model raises the same idea a level — from "is this message well-formed" to "does this asset actually do what it claims."
When an asset declares implements: [AvailabilityMachine], the platform (PREKIT) checks it. For every required signal, coverage comes back ok, missing, or wrong_type. A machine that claims to be availability-bearing but never exposes machine_status shows up as missing — automatically, on every asset, without anyone auditing a PDF. A tag that is not part of the standard will also be published to the namespace - that is our philosophy of gradual standardization - but the topic is clearly marked as non-standard.
That gap report is not a failure mode; it's the most useful thing the model produces. It's a live map of exactly where reality and the standard disagree — which line uses a different tag name, which site never wired the status at all. The standard stops being a document you hope people followed and becomes a query you can run against the fleet. And declaring the type is generative: the asset's required tag slots (signals) are provisioned for you, so the path of least friction doesn't just validate the standard — it builds the skeleton of it.
The model lives in the customer's repo
Asset types as code only pay off if the code has an owner and a home. The split we use: the platform ships a small set of base interfaces — Machine, a discrete-parts OEEProducer, a few others — and the customer owns everything specific to their operation in their own versioned repository. A customer interface is a YAML file that extends a platform base and adds what their machines actually do. It's loaded at deploy time and resolved together with the platform interfaces.
Concretely, that means the customer's asset model is a git repo we build with them, not a spec we hand to them. The first workshop is mostly the conversation Part 1 was about — what is a "machine" here, what states is it allowed to be in, when does a changeover begin — and the output of that conversation is committed as interface definitions. From then on, every change to "what a machine is" is a reviewable diff with an author and a date, deployed through the same flow as the rest of the topology, with drift detection against what's actually live. The data model stops being tribal knowledge in three engineers' heads and becomes a versioned artefact the customer keeps.
And because the resolved interfaces, each asset's declared types, and its live conformance are all published back into the namespace as retained topics, the model is discoverable from inside the UNS itself. You can subscribe to the namespace and ask it what asset types exist, what each machine claims to be, and whether it's actually meeting that claim.
The scale curve, and the KPI you can finally trust
Setting up the first standardized asset is genuinely more work than just publishing tags. You have to agree on the vocabulary — what running means, when a setup begins, what state a line is in during a planned changeover. Those conversations don't happen for free, and on the first line they feel like overhead.
The second line costs much less. Most of the agreements carry over; you're applying the same asset type to a different physical instance. The third costs less than the second. By the fifth, the work of adding one is the wiring, not the modeling.
Then the real payoff arrives. Take machine availability — the KPI from Part 1 that everyone wanted and nobody trusted, because "available" was computed three different ways on three different lines. Once availability is defined against the interface — derived from one standardized machine_status that every availability-bearing asset is validated to expose — you develop and deeply validate that logic on a single line, confirm coverage is green, and roll it out to every asset that implements the type. Every consumer downstream keeps working, because the contract did not change; what changed is one well-tested definition, applied everywhere at once.
That's the moment people start trusting the number. That's because a change to the definition of "available" is now something you can see applied to all assets, and verify. The KPI stops being one team's spreadsheet and becomes a fleet-wide capability. This is the difference between ten machines on the UNS and a machine-availability capability deployed ten times. The first is a pile. The second is a platform — which is exactly what we set out to build back in Part 1, when the only thing standing between a namespace and a swamp was a standard somebody was willing to enforce.
And it isn't only dashboards — this is also the moment your data scientists' work starts to scale. Without a data model, every analysis is a one-off: before anyone builds an anomaly detector or a predictive-maintenance model, someone spends a week finding the right tags on this machine, reverse-engineering what its values mean, and cleaning the result by hand — then redoes all of it, from scratch, for the next machine. When assets conform to a known type, that overhead is gone. A model written against AvailabilityMachine, or against OEEProducer's part_counter, runs unchanged on every asset that implements the type — and the platform tells you up front which assets qualify and which fall short. Your data scientists stop doing archaeology one machine at a time and start building capabilities that deploy across the fleet; their effort compounds instead of resetting with every new line. That is the shift from one-off tasks to systems that scale.
If you're working on the asset-model question in your own UNS, fork franzmq for the schematic layer and build up from there — or build your own equivalent. Either way, we'd be glad to compare notes.
Making a Unified Namespace trustworthy — a three-part series: 1 · UNS vs Data Swamp · 2 · Data Contracts · 3 · Asset Models.
For deeper background, see our earlier post on What Exactly Is a Unified Namespace? and on bridging the gap between ERP and the shopfloor.
