RFC 9880 and the IoT Validation Problem

From Standards Fragmentation to a Device-Model Compiler

The IoT/IIoT world has a data model problem and the concept of Digital Twin has made this issue more important. Plenty of people have tried to fix it. RFC 9880 and its Semantic Definition Format (SDF) are a serious attempt: a JSON-based, ecosystem-neutral way to describe what a device is and what it does. I have had some experiences with this specification, and I’ve understood that the format shows its full value once you treat it as source code, something you compile.

The IoT/IIoT world has a data model problem and the concept of Digital Twin has made this issue more important. Plenty of people have tried to fix it. RFC 9880 and its Semantic Definition Format (SDF) are a serious attempt: a JSON-based, ecosystem-neutral way to describe what a device is and what it does. I have had some experiences with this specification, and I’ve understood that the format shows its full value once you treat it as source code, something you compile.

In this article we’ll look at why five IoT ecosystems describe the same thermostat in five incompatible ways, where SDF comes from, and why it ended up looking the way it does. Then I want to share what came out of building a validator around it. SDF validation can be viewed as a compilation problem, and the most useful part of the pipeline turned out to be the intermediate format the engine produces, not the engine itself.

Five ecosystems, five ways to say "thermostat"

In production IoT (think of a plant with thousands of heterogeneous endpoints), protocols like the Constrained Application Protocol (CoAP), MQTT, and HTTP-over-TLS are rarely the problem. The trouble starts when two devices measure the same physical quantity and describe it in ways that don’t line up.

Take temperature. A Lightweight Machine-to-Machine (LwM2M) sensor reports object 3303, resource 5700, a float in Celsius. An Open Connectivity Foundation (OCF) sensor exposes /temperature with "temperature": 22.5, "units": "C". A Matter device uses a TemperatureMeasurement cluster with MeasuredValue expressed in hundredths of a degree. Same quantity, three representations, and that is only three of the five major ecosystems. No amount of unit conversion makes the problem go away. Each ecosystem carries its own vocabulary for what a device is, does, and means: IPSO, Open Mobile Alliance (OMA) LwM2M, OCF, W3C Web of Things (WoT) Thing Description, Matter. Five ontologies for one reality.

The bill grows linearly with the ecosystems you support. Every protocol needs its transport adapter plus a data-model adapter, and those adapters are brittle because the structural assumptions end up scattered through the application code instead of living in a shared schema.

Where SDF comes from: a short history of One Data Model

SDF is the output of several years of cross-industry work, and knowing that history explains a lot about why the format looks the way it does.

The story starts at a Zigbee "Hive" meeting in the fall of 2018. In that room a cross-industry consensus formed around a single observation: the lack of a common IoT data model was a real inhibitor to the growth of the whole market. Interoperability existed inside each ecosystem, but crossing ecosystems meant paying the integration tax over and over.

The response was the One Data Model (OneDM) liaison group. There’s a detail here I’ve always liked. Rather than spin up yet another legal standards organization, the participants deliberately chose to cooperate under a liaison. Through 2019, the work involved a broad group of standards bodies and vendors: OMA SpecWorks, OCF, the Zigbee Alliance, Bluetooth SIG, and others. Ericsson, for instance, contributed tooling to translate its IPSO/LwM2M models.

The group’s goal was concrete and ambitious: arrive at a common set of data and interaction models for IoT devices, so that ideally, for each class of device, there is one model everyone can adopt. But to align models contributed by organizations that each described things in their own way, they first needed a shared way to write a model down. That shared way is the Semantic Definition Format.

Building a new format was a deliberate decision. Every participating organization already had a description mechanism, but each one was tailor-made for that organization’s needs, and bending an existing one into a neutral lingua franca looked harder than starting fresh. So SDF was built on top of existing JSON machinery (CDDL and the json-schema.org proposals) rather than inventing a new syntax, precisely so that ordinary JSON tooling could produce and consume it.

In July 2020, SDF was presented to the Internet Engineering Task Force (IETF), which kicked off the chartering of the ASDF Working Group ("A Semantic Definition Format"). Standardization moved into the open, models were contributed under a BSD-3 license into a public "playground" repository, and round-tripping (model → SDF → model) was used to prove SDF could carry the semantics of existing ecosystems without information loss.

The base specification landed as RFC 9880, Semantic Definition Format (SDF) for Data and Interactions of Things, published on the Standards Track in January 2026. Its editors, Michael Koster, Carsten Bormann, and Ari Keränen, are the same names that carried the work through the ASDF WG.

The work has not stopped at the base specification. OneDM maintains public repositories of SDF models, from connected light bulbs to industrial energy meters, and the ASDF Working Group keeps refining the mapping between SDF and W3C WoT Thing Models in Semantic Definition Format (SDF): Supplements.

What SDF adds on top of JSON Schema

SDF builds on JSON Schema: it inherits the type system, the numeric and string constraints, and the composition mechanisms. On top of that it adds three categories of concepts that JSON Schema does not have, and arguably could not have, because they live at a different level of abstraction.

The first is interaction affordances. A device is more than a JSON document; it’s something you interact with. SDF formalizes three interaction types. An sdfProperty is a quantity you read and optionally write: the current temperature, a brightness level, the state of a valve. An sdfAction is an operation invoked with an input that produces an output ("set the setpoint to 22°C", "run a calibration cycle"). An sdfEvent is an asynchronous notification ("alarm threshold exceeded", "battery below 10%"). This tripartition mirrors the W3C Web of Things Thing Description interaction model, and the alignment was intentional: bidirectional translatability with WoT TD was one of SDF’s stated goals.

The second is hierarchical composition. An sdfObject groups the affordances of a logical component: a bulb, a humidity sensor, an actuator. An sdfThing groups objects into a complete device, say a smart thermostat with a temperature sensor, a heating actuator, and a display. The sdfRef mechanism lets you reuse common definitions across models and override specific attributes through JSON Merge Patch semantics (RFC 7396).

The third is data qualities: semantic metadata that qualifies each property beyond its type. Is it readable, writable, observable, nullable? JSON Schema has no equivalent, and these are exactly what you need to generate correct protocol bindings. A non-writable property should not expose a write endpoint. An observable property must support subscriptions. A nullable property has to accept absent values instead of erroring out.

Where SDF makes a concrete difference

To see why SDF helps, we can look at where the absence of a shared data model actually hurts:

  • Multi-protocol gateways. Aggregating Modbus, BACnet, LwM2M, and MQTT means normalizing payloads before they go upstream, which today is custom code per protocol/device pair. Load the SDF model and the gateway knows what to expect for every property, so it can validate and transform generically.
  • Digital twins. A twin has to replicate the device’s data model. If that model is formal and machine-readable, the twin can be generated; if it lives in a 200-page PDF, every twin is a fresh integration project.
  • Certification. A lab can turn SDF profiles into auto-generated test suites that check every property against its declared constraints.
  • Cross-vendor systems. Three vendors' bulbs, one SDF "Light" model, and the BMS treats them alike. Brightness is an integer between 0 and 100, so 150 or "bright" is caught on arrival.
  • Edge pre-validation. Where bandwidth to the cloud is scarce, checking before sending keeps malformed payloads from wasting the whole pipeline.

The gap: from specification to runtime validation

All of these scenarios need to use the model to check real data. SDF is a rich format, but the available tooling covers mostly the syntactic validation of SDF documents themselves: is the model valid JSON, do the sdfRef references resolve, does the structure conform to the SDF meta-schema? Necessary work, but it stops short of the interesting part: concrete applications of the RFC’s potential, such as automatic code generation and data validation!

I wanted to focus on a thorny topic: the validation of telemetry against an SDF model. Take an SDF document, turn it into a structure optimized for validation, and use it to check, at high speed, the payloads devices actually send at runtime.

That turns out to be a different problem from classic JSON Schema validation, for at least three reasons.

Start with dispatch. A gateway receives payloads targeting hundreds of different affordances, each with its own schema. The validator has to map each payload to its affordance in constant time.

Then there’s volume. In industrial contexts you easily reach hundreds of thousands of messages per second. A validator that allocates objects on every call creates garbage collector pressure that translates into latency pauses, unacceptable in a soft real-time system.

And finally configurability. In production, validation is not binary. A sensor going offline sends null: error, or expected? A device adds an undeclared field: reject it or ignore it? A value outside range: hard error or warning? The answers change by deployment, by customer, by environment.

SDF validation is essentially a compilation problem

I’ve borrowed an idea from programming-language compilers: the SDF document plays the part of source code; the compiled model is the bytecode you produce from it; the validation engine is a virtual machine executing that bytecode; the policies are the flags you pass to the compiler. The pipeline is linear, and each stage hands a smaller, faster artifact to the next.

Phase 1, Compile. Parse the SDF document once. Resolve sdfRef references recursively (with JSON Merge Patch semantics for overlays), extract and index the affordances, and lower the JSON Schema of each one into a typed intermediate representation: a tree of schema nodes specialized by kind (scalar, object, array, choice, void). The output is the compiled model: an immutable structure, detached from the source document, that carries everything validation needs. Expensive, but you pay it once, or zero times if you load it from cache.

Phase 2, Bootstrap. Feed the compiled model to a bootstrap step that builds a tree of validators, one per schema node, injecting the configured policies. This is the pivot of the whole design. A single compiled model can produce validation engines with completely different behavior just by swapping policies: a strict engine for the test lab, a permissive one for production, one with custom format validators for a specific domain. Bootstrap is cheap, and runs once per policy configuration.

Phase 3, Validate. The engine exposes one operation: given an SDF JSON Pointer and a payload, return a structured result. Dispatch from pointer to the right validator is an associative-array lookup, O(1). Validation then recurses down the validator tree with no schema allocation, no JSON parsing, no reference resolution. Everything was pre-computed upstream.

Compilation is expensive but runs at most once. Bootstrap is cheap and runs once per configuration. Only validation sits on the latency-critical path, and by then everything it needs is already optimized. Like any good compiler, the pipeline is only as good as the work it can shift from runtime to compile time.

Where nanoseconds matter

Experimenting with a validation architecture in Java and SDF meant devising and testing several designs. In a validator meant to sustain millions of ops per second, we are forced to adopt some micro-optimizations. Here are a few that paid off, and the principles apply beyond SDF too.

Zero-allocation on the hot path. Validating an RFC 3339 date-time is usually done with a date parser or a regex, and both allocate on every call. A character-level parser that walks the string (separators, month/day ranges, leap years) allocates nothing: in my benchmarks, it reached tens of millions of validations per second with no GC pressure. The same trick works for base64: check the alphabet and the padding character by character instead of decoding the bytes.

Lazy error paths. The instance path, meaning where in the payload the error occurred, only matters when validation fails. Build it eagerly and you concatenate strings at every level for nothing. Build it on demand, walking back up the context chain, and the happy path costs you nothing.

Two smaller ones helped as well: since SDF makes properties readable, writable, and observable by default (and data nullable by default), one shared singleton covers the default case, and wherever the count is known up front, collections are allocated at final capacity. Basic stuff, often skipped, but it measurably helps.

Composable policies: validation is not black and white

What sets IoT validation apart from document validation is that "correctness" depends on context: the same null, undeclared field, or out-of-range value can be an error in one deployment and perfectly normal in another.

So the engine carries a layer of composable policies: independent subsystems, each responsible for a single decision. Null handling is one policy. Treatment of undeclared properties is another. String-format validation (dates, URIs, emails, hostnames) is a dedicated policy that can enable, disable, or replace individual format validators.

Each policy is a functional interface: implement it with a lambda, compose it with others, or reach for the provided presets (strict for testing, permissive for production). Composition is additive, so changing one policy does not disturb the rest. The result is a validator you adapt without forking and without boolean flags scattered through the code.

The intermediate format is the real product

Of everything in that pipeline, the piece that proved most valuable is the compiled model, once you treat it as an intermediate format in its own right.

Because the compiled model is self-contained, it can be serialized to JSON, persisted to a filesystem or a database, and deserialized to recreate an identical validation engine without ever touching the original SDF document again. That single property unlocks a set of very concrete deployments:

  • A centralized compilation service processes SDF models once and ships compiled models (possibly in a binary form) to edge gateways.
  • A cache deserializes the compiled model on startup instead of recompiling from source every time.
  • A CI/CD pipeline compiles models as build artifacts and distributes them like any other dependency.

Document the intermediate format in a language-agnostic way and it becomes a contract. A compiler written in one language and a validation engine written in another can interoperate purely through the serialized model.

Conclusion

RFC 9880 is a young standard, and its real impact will depend on adoption. But the problem it addresses, the fragmentation of IoT data models, is real, and the ecosystems behind OneDM spent years agreeing that it needed solving. The proliferation isn’t slowing down either: with Matter, Thread, and new industrial profiles, it’s speeding up.

Creating an SDF validator answered a practical question: if a formal device model exists, how can I use it to protect my system from invalid data, at a speed compatible with real-world IoT volumes and with enough configurability to reflect the complexity of real-world implementations?

The answer I found is that SDF validation leads to a process made of compilation, configuration, and a runtime library: you compile up front and validate where it’s actually needed. And of everything that process produces, the most valuable artifact is not the engine: it is the compiled model in the middle.