NAMUR Open Architecture (NOA): A Hands-On Second-Channel Monitoring Setup (2026)

NAMUR Open Architecture (NOA): A Hands-On Second-Channel Monitoring Setup (2026)

NAMUR Open Architecture (NOA): A Hands-On Second-Channel Monitoring Setup (2026)

Every process plant that has tried to bolt analytics onto a running DCS eventually hits the same wall: the control system is safety-rated, change-controlled, and untouchable, yet the data locked inside it is exactly what the reliability and optimization teams need. NAMUR NOA architecture resolves that tension by adding a second channel for monitoring and optimization data that leaves the core control loop completely untouched — no new I/O in the safety envelope, no firmware changes on the controllers, no fresh attack surface pointed at the process. Instead, data flows out through a one-way diode into a separate domain where you can run condition monitoring, energy models, and machine-learning workloads at cloud pace. This tutorial builds that second channel end to end: a read-only OPC UA export, an edge collector, a logical data diode into a Unified Namespace, condition-monitoring analytics, and the Verification of Request (VoR) gate that governs the rare case where you want to write something back.

What this covers: the NOA concept in NE 175, the two-domain split, the diode and aggregating server, VoR, and a runnable read-only-export-to-analytics pipeline you can stand up on a bench.

Context and Background

NAMUR — the international user association of automation technology in process industries — published Recommendation NE 175 to formalize the NOA concept after several years of working-group drafts. The problem it addresses is structural. The classic automation pyramid was designed for deterministic control, and it is very good at that. But it makes every new data consumer a project: each historian tag, each analytics feed, each cloud connector must be engineered inside the same change-controlled system that runs the plant. That coupling is why so many Industrial IoT pilots stall — the moment “add a sensor for the ML model” becomes “modify the safety instrumented function’s host,” the project dies in the management-of-change queue.

NOA’s answer is deliberately conservative. It does not replace the DCS, and it is explicit on that point: the core process control domain keeps doing exactly what it does, and NOA is a complement layered beside it. The NAMUR focus page on Open Architecture frames the goal as making production data “easily and securely usable” for monitoring and optimization “with no impact on the automation system.” The mechanism is a second, parallel path out of the plant, secured so that data can leave but commands cannot casually enter.

This idea rhymes with the Unified Namespace pattern using HiveMQ and Sparkplug B that IT/OT convergence teams already know — a single, event-driven source of truth for plant state. NOA is the process-industry governance model that makes feeding such a namespace safe. It also sits alongside newer field-level work like OPC UA FX field exchange; NOA cares about the monitoring channel, not the control-to-control traffic FX standardizes.

The NOA architecture: two domains, one diode

NOA divides the plant into two domains with a strictly governed boundary between them. Everything that must be deterministic, certified, and change-controlled stays in the Core Process Control (CPC) domain. Everything that benefits from openness, speed, and iteration — dashboards, condition monitoring, energy optimization, ML — lives in the Monitoring and Optimization (M+O) domain. Between them sits the NOA diode, which enforces one-way data flow out of the CPC, and the Verification of Request gate, which is the only sanctioned path for anything to flow back.

NAMUR NOA architecture with two domains and a one-way diode between core process control and monitoring

Figure 1: The NOA two-domain model. The CPC domain (field instruments and DCS) exports read-only data through the NOA diode into the M+O domain, where an aggregating server fans it out to analytics. The only reverse path is a verified write through VoR.

Figure 1 shows the whole shape at a glance: control data flows left to right through the diode into the aggregating server and on to analytics, while any write-back must climb back through Verification of Request as a separate, heavily-checked path.

NOA’s building blocks, per NE 175, are four: a standardized NOA information model based on OPC UA, the NOA diode, the NOA Verification of Request component, and the concept of the NOA Aggregating Server. Each has a job that maps cleanly onto a layer of the pipeline you will build below.

The diode enforces asymmetry, not just a firewall rule

The word “diode” is chosen carefully. A firewall permits or denies traffic by policy, and policy can be misconfigured, bypassed, or subverted. A diode makes the reverse direction structurally impossible or, at minimum, verifiably absent. In the strongest form this is a hardware data diode — an optical link with a transmitter on the CPC side and a receiver on the M+O side and no return fiber at all — which is why vendors such as Advenica and genua market physical diodes specifically for NOA deployments.

You do not always need hardware. NOA also recognizes a logical diode: an OPC UA server that exposes every relevant node as read-only, so that even a fully-authenticated client on the M+O side has no writable attribute to target. The OPC Foundation itself notes that “by configuring a parameter as read-only, it cannot be written,” giving the server direction control in software. The trade-off is trust: a logical diode is only as strong as the server’s access-control implementation and its patch state, whereas a hardware diode’s asymmetry survives a total software compromise of the collector. For a first deployment, and for the worked example in this tutorial, a logical diode plus network segmentation is a defensible starting point; for a diode protecting a safety-critical unit, budget for the hardware.

The aggregating server presents one interface

Fan-in matters. A large plant might have dozens of diodes — one per unit, per skid, per legacy island. If every M+O application had to know about each diode’s address space, the integration cost would reappear on the other side of the boundary. The NOA Aggregating Server solves this by structuring the communication lines from the different diodes so the M+O applications “only see one interface.” It is the place to implement authentication, authorization, load balancing, and caching for the whole monitoring channel, and it is where the NOA information model gets normalized so a vibration reading from a 2003-vintage controller and one from a 2025 smart transmitter look alike to the analytics above.

Concretely, the aggregating server is also the buffer that decouples two very different rate regimes. The CPC side may push subscription updates at 200-millisecond intervals for hundreds of nodes, while an M+O analytics job might poll once a minute and a cloud historian might batch every fifteen. If each application connected straight through to a diode, the diode — and the controller behind it — would carry the union of every consumer’s request pattern, which is exactly the load NOA promised the CPC would never see. The aggregating server absorbs that: it maintains a single upstream subscription per diode, caches the last-known value and quality per node, and answers every downstream request from cache. A slow consumer, a reconnecting dashboard, or a misbehaving analytics container never reaches back to the controller. This is also where you enforce a per-consumer read quota so that no single M+O application can, through sheer request volume, degrade the experience for the others.

The information model carries meaning, not just numbers

A raw OPC UA tag like ns=3;s=TT_4711.PV means nothing to an ML pipeline that was trained on another plant. NOA leans on OPC UA information modeling — and on companion work such as PA-DIM, the Process Automation Device Information Model now co-owned by NAMUR, the OPC Foundation, FieldComm Group, VDMA, ZVEI and others — so that a temperature is typed as a temperature, with an engineering unit, a range, and a semantic identifier (from IEC’s Common Data Dictionary or ECLASS). That semantic layer is what lets a condition-monitoring model be portable across assets instead of hand-mapped per plant.

To make this concrete: PA-DIM defines a common set of parameters — device serial number, manufacturer, hardware and software revision, and process-value attributes with their engineering units and ranges — under a stable OPC UA type hierarchy, and it attaches semantic identifiers so that “process value” carries a machine-readable meaning independent of the vendor’s tag naming. A NOA aggregating server that presents its nodes as PA-DIM types gives every M+O application a predictable shape to program against. The alternative — each analytics team reverse-engineering what TT_4711.PV means, what its range is, and whether its unit is Celsius or Fahrenheit — is precisely the per-plant integration tax that kills reuse. The modeling effort is front-loaded and unglamorous, but it is the mechanism by which a fleet-wide anomaly model trained on one compressor can be dropped onto another without a rewrite.

Hands-on: building the second channel

Now the tutorial proper. The goal is a pipeline that reads process values from a controller through a read-only OPC UA surface, moves them across a logical diode into a Unified Namespace on MQTT, and runs condition-monitoring analytics on the M+O side — all without a single writable path into the CPC. Figure 2 is the concrete data path you are about to wire up.

Read-only OPC UA export flowing through an edge collector and logical diode into an MQTT Unified Namespace

Figure 2: The runnable pipeline. A controller or gateway exposes a read-only OPC UA server; an edge collector subscribes; a logical diode client forwards to an MQTT broker (the UNS); a time-series store and condition-monitoring analytics consume from there.

Figure 2 maps each hop to a component you will configure: the read-only server, the collector’s subscription, the diode client that only ever publishes outward, and the broker that becomes the M+O domain’s single source of truth.

Step 1 — Expose a read-only OPC UA surface

Assume the controller or a protocol gateway already speaks OPC UA. The critical configuration is that the NOA-facing endpoint is read-only. On many gateways this is a per-node or per-namespace flag; below is the shape of a hardened endpoint definition (a open62541-style server descriptor is used for illustration — adapt the key names to your stack):

# noa-export-endpoint.yaml  — the read-only surface on the CPC side
endpoint:
  url: "opc.tcp://gateway.cpc.local:4841/noa"
  security_policy: "Basic256Sha256"          # never None on a real plant
  message_security_mode: "SignAndEncrypt"
  user_token: certificate                     # mutual TLS, no anonymous
namespace:
  index: 3
  access_level_default: CurrentRead            # READ only — no CurrentWrite
nodes:
  - node_id: "ns=3;s=TT_4711.PV"
    display_name: "Reactor inlet temperature"
    eu: "degC"
    eu_range: { low: 0.0, high: 250.0 }
    access_level: CurrentRead
  - node_id: "ns=3;s=PT_2201.PV"
    display_name: "Feed pump discharge pressure"
    eu: "bar"
    eu_range: { low: 0.0, high: 40.0 }
    access_level: CurrentRead
  - node_id: "ns=3;s=VT_9002.PV"
    display_name: "Feed pump vibration RMS"
    eu: "mm/s"
    eu_range: { low: 0.0, high: 25.0 }
    access_level: CurrentRead

Two details make this a diode rather than a leak. First, access_level is CurrentRead only — there is no CurrentWrite bit on any node, so a write service call returns BadNotWritable regardless of the client’s rights. Second, the endpoint refuses anonymous and unencrypted sessions. Confirm the read-only claim empirically before trusting it — a one-line opcua CLI probe is enough:

# Prove the node rejects writes (expect BadNotWritable / BadUserAccessDenied)
python -m asyncua.tools.uawrite \
  --url opc.tcp://gateway.cpc.local:4841/noa \
  --nodeid "ns=3;s=TT_4711.PV" \
  --value 999 --datatype float

If that command succeeds, you do not have a diode — stop and fix the access level before going further.

Step 2 — Subscribe from the edge collector

On the M+O side, an edge collector subscribes to the read-only nodes and never issues a write. Using the asyncua Python client, a minimal collector that pulls monitored items via subscription (not polling) looks like this:

# collector.py  — M+O side. Subscribe-only; there is no write() anywhere.
import asyncio
from asyncua import Client, ua

CPC_URL = "opc.tcp://gateway.cpc.local:4841/noa"
NODES = {
    "reactor/inlet/temperature": "ns=3;s=TT_4711.PV",
    "feedpump/discharge/pressure": "ns=3;s=PT_2201.PV",
    "feedpump/vibration/rms": "ns=3;s=VT_9002.PV",
}

class DiodeHandler:
    def __init__(self, sink):
        self.sink = sink  # called for every sample; forwards outward only
    def datachange_notification(self, node, val, data):
        src = data.monitored_item.ClientHandle
        self.sink(str(node.nodeid), val,
                  data.monitored_item.Value.SourceTimestamp)

async def run(sink):
    async with Client(url=CPC_URL) as client:
        await client.set_security_string(
            "Basic256Sha256,SignAndEncrypt,"
            "client-cert.pem,client-key.pem")
        handler = DiodeHandler(sink)
        sub = await client.create_subscription(200, handler)  # 200 ms
        for _, nid in NODES.items():
            node = client.get_node(nid)
            await sub.subscribe_data_change(node)
        while True:
            await asyncio.sleep(1)

The design intent is enforced by omission: this process has no code path that writes to the CPC server. It opens a session, creates a subscription, and forwards. A code review of the collector is part of the diode’s assurance case — if there is no write call and no reverse socket, the reverse direction is structurally absent even before you add a hardware diode.

Step 3 — Cross the logical diode into the Unified Namespace

The collector forwards each sample to an MQTT broker that serves as the M+O Unified Namespace. Publishing is the only outward operation. Note the topic structure: an ISA-95-style hierarchy that gives every value a self-describing address, exactly the semantic-carrying idea NOA’s information model pushes.

# diode_sink.py  — turns collector callbacks into MQTT publishes (outward only)
import json, paho.mqtt.client as mqtt

BROKER = "uns-broker.mo.local"
PLANT  = "acme/site1/reactor-a"

mq = mqtt.Client(client_id="noa-diode", protocol=mqtt.MQTTv5)
mq.tls_set()                       # M+O side still uses TLS
mq.connect(BROKER, 8883, keepalive=30)
mq.loop_start()

# Map raw OPC UA node ids to clean UNS topics
TOPIC = {
    "ns=3;s=TT_4711.PV": f"{PLANT}/reactor/inlet/temperature",
    "ns=3;s=PT_2201.PV": f"{PLANT}/feedpump/discharge/pressure",
    "ns=3;s=VT_9002.PV": f"{PLANT}/feedpump/vibration/rms",
}

def publish(node_id, value, ts):
    topic = TOPIC.get(node_id)
    if topic is None:
        return
    payload = json.dumps({
        "value": float(value),
        "ts": ts.isoformat(),
        "quality": "Good",
        "source": "noa-diode",
    })
    mq.publish(topic, payload, qos=1, retain=True)   # retain = last-known

Wire publish in as the collector’s sink:

# run.py
import asyncio
from collector import run
from diode_sink import publish
asyncio.run(run(publish))

At this point data is flowing out of the CPC, across a read-only boundary, and into a broker that any number of M+O applications can subscribe to — all without a single sanctioned reverse path. That is the NOA second channel in working form.

Step 4 — Run condition-monitoring analytics on the second channel

The payoff of the second channel is that you can now run whatever analytics you like without touching the plant. Figure 4 shows a lightweight condition-monitoring flow for the feed pump: extract features from the vibration and temperature streams, compare against a learned baseline, and emit a health score that itself gets published back into the UNS (never into the CPC).

Condition monitoring on the NOA second channel producing a health score published to the Unified Namespace

Figure 4: Second-channel condition monitoring. Raw vibration and temperature become features, a baseline model produces a residual, and a thresholded health score raises early-warning or maintenance alerts published to the UNS — all in the M+O domain.

Figure 4 makes the closed loop explicit: analytics consume from the UNS and publish back to the UNS, so insights circulate in the M+O domain without ever reaching for a writable CPC tag.

A minimal detector that subscribes to the vibration topic, keeps a rolling baseline, and raises a health signal:

# cm_analytics.py  — runs entirely in the M+O domain
import json, statistics, collections
import paho.mqtt.client as mqtt

PLANT = "acme/site1/reactor-a"
VIB   = f"{PLANT}/feedpump/vibration/rms"
HEALTH = f"{PLANT}/feedpump/health"

window = collections.deque(maxlen=600)   # ~2 min at 200 ms
ISO10816_ALARM = 7.1                       # mm/s, illustrative class threshold

def on_message(client, _u, msg):
    v = json.loads(msg.payload)["value"]
    window.append(v)
    if len(window) < 30:
        return
    mu = statistics.fmean(window)
    sigma = statistics.pstdev(window) or 1e-6
    z = (v - mu) / sigma
    if v > ISO10816_ALARM:
        state = "maintenance_alert"
    elif z > 3:
        state = "early_warning"
    else:
        state = "normal"
    client.publish(HEALTH, json.dumps(
        {"state": state, "rms": v, "z": round(z, 2)}),
        qos=1, retain=True)

c = mqtt.Client(client_id="cm-feedpump", protocol=mqtt.MQTTv5)
c.tls_set(); c.on_message = on_message
c.connect("uns-broker.mo.local", 8883)
c.subscribe(VIB, qos=1); c.loop_forever()

The ISO10816_ALARM = 7.1 value is illustrative — real machine classes and mounting conditions set their own thresholds under ISO 20816 (the successor to ISO 10816), so treat that constant as a placeholder you calibrate against the specific asset, not a universal number. The point is architectural: a genuinely useful reliability signal now exists, and it was produced without a management-of-change ticket against the DCS.

Verification of Request: the only way back in

Sometimes monitoring wants to act — nudge a setpoint from an optimizer, acknowledge a soft alarm, push a new model coefficient. NOA does not forbid this, but it refuses to let the M+O domain write to the CPC directly. Every reverse action must pass Verification of Request (VoR), whose job is to ensure requests come from trusted M+O services, are syntactically and semantically clean, and cannot flood or destabilize the control system. VoR is the deliberate, narrow, heavily-audited counterpart to the wide-open outward diode.

Verification of Request sequence gating a write-back from a monitoring app into the DCS

Figure 3: The VoR write-back sequence. An M+O app proposes a change; VoR performs syntax, range, rate-limit and authorization checks and routes it for operator or rule-based approval; only an approved request reaches the DCS, and the result status returns to the app.

Figure 3 traces one governed write: the proposal enters VoR, survives every check and an approval gate, and only then reaches the DCS — with the outcome reported back so the optimizer knows whether its request landed.

A sketch of a VoR gate makes the checks concrete. Note that it proposes to the DCS through a separate, authenticated, minimal-surface channel — not through the read-only diode, which stays read-only:

# vor_gate.py  — the ONLY sanctioned reverse path, heavily checked
import time, collections

# Per-tag safe envelope and rate policy (engineering-owned config)
POLICY = {
    "TC_4711.SP": {"low": 40.0, "high": 210.0, "max_per_hour": 6},
}
_recent = collections.defaultdict(collections.deque)

class VoRReject(Exception):
    pass

def verify_and_forward(request, principal, dcs_writer, approver):
    tag = request["tag"]; val = float(request["value"])
    pol = POLICY.get(tag)
    if pol is None:
        raise VoRReject("tag not in VoR allow-list")
    if not principal.get("trusted"):
        raise VoRReject("untrusted M+O principal")
    if not (pol["low"] <= val <= pol["high"]):
        raise VoRReject("value outside safe envelope")

    # Rate limit: refuse request flooding
    now = time.time()
    q = _recent[tag]
    while q and now - q[0] > 3600:
        q.popleft()
    if len(q) >= pol["max_per_hour"]:
        raise VoRReject("rate limit exceeded")

    # Human or rule-based approval before anything reaches the DCS
    if not approver(request, principal):
        raise VoRReject("approval denied")

    q.append(now)
    return dcs_writer(tag, val)   # authenticated, audited, minimal surface

Three properties matter here. The allow-list means only a tiny, engineering-approved set of tags can ever be targeted — an optimizer cannot invent a new writable point. The safe envelope and rate limit mean even a compromised-but-trusted optimizer cannot drive a setpoint to a dangerous value or hammer the controller. And the approval step keeps a human or a certified rule engine in the loop for anything consequential. VoR is where NOA earns the right to be called safe: the outward channel is generous, the inward channel is paranoid, and the two are never the same wire.

It is worth being precise about what the safe envelope defends against, because it is not only malice. Consider a real-time optimizer computing a reactor-temperature setpoint from a model. If a sensor feeding that model drifts, or an input arrives with bad quality that the optimizer fails to reject, the model can output a numerically valid but physically absurd setpoint — 240 °C on a vessel rated for 210. A firewall would pass it; the DCS, trusting a “verified” source, might apply it. The VoR envelope is the last-line clamp that refuses it regardless of who asked, because the request falls outside the engineering-owned low/high band for that tag. This is why the envelope lives in VoR and not in the optimizer: the optimizer is the thing that might be wrong, so it cannot be the thing that guards itself.

The rate limit plays an analogous role against instability rather than magnitude. Even in-band setpoint changes, if issued rapidly, can excite oscillation in a loop whose tuning assumes human-paced adjustments. Capping writes to a handful per hour per tag forces the M+O domain to behave like an advisor, not a co-controller. Where a use case genuinely needs fast, frequent closed-loop action, that is a strong signal the function belongs inside the CPC under proper control engineering — not stretched across a diode. NOA is honest about this boundary: it opens the plant’s data generously, but it keeps real-time control where real-time control belongs. The reverse channel exists for occasional, deliberate, supervised nudges, and the design actively discourages anything more.

Trade-offs, Gotchas, and What Goes Wrong

The most common failure is treating a logical diode as if it were a hardware one. A read-only OPC UA server is only read-only while its access control is correctly configured and its software is unbroken. A vulnerability in the server, a misapplied namespace default that leaves CurrentWrite set, or an operator who “temporarily” enables writes for a commissioning test can all silently turn the diode into a two-way street. If the unit behind the diode is safety-critical, the logical-only approach is under-engineered; pair it with a hardware diode so asymmetry survives software compromise.

A second trap is scope creep across the boundary. NOA works because the CPC stays boring and the M+O domain absorbs all the change. Teams routinely erode this by pushing “just one” analytics-derived value back into the historian that the control logic then reads — quietly recreating the coupling NOA exists to prevent. Every reverse dependency must go through VoR, full stop; a back door that bypasses VoR is not a shortcut, it is the reintroduction of the original problem.

Third, semantics do not appear for free. Publishing raw ns=3;s=... node IDs into a UNS produces a namespace nobody downstream can use portably. Without the NOA information model or an equivalent typing layer, every analytics app re-implements per-tag mapping, and the integration cost you removed at the DCS reappears in the analytics team. Budget the modeling work; it is the difference between a data swamp and a reusable asset.

Finally, watch retained-message staleness and quality propagation on the MQTT side. A retain=True last-known value is convenient but can mislead an analytics job into treating a stale reading as live after a collector outage. Propagate OPC UA quality codes into the payload (as the example does) and have consumers treat non-Good quality and stale timestamps as gaps, not data.

There is also a subtler timekeeping hazard. OPC UA carries two timestamps per value — a source timestamp set where the value originated and a server timestamp set when the server processed it — and the example forwards the source timestamp deliberately. If a collector or aggregating server ever substitutes its own clock, a condition-monitoring model that reasons about rate-of-change or phase will silently learn from the wrong time base, and the error is invisible until an event investigation asks “when did this actually happen.” Pin the source timestamp end to end, keep the collector’s clock disciplined by NTP or PTP, and record both timestamps in the historian so that latency through the second channel is itself measurable rather than assumed.

One more anti-pattern deserves a name: the “monitoring value that becomes a control input.” An optimizer publishes a suggested setpoint to the UNS as advisory information; six months later someone wires a controller to subscribe to that topic “just to save a step.” The moment the CPC reads an M+O-domain value, the domains are coupled, the diode’s guarantee is void, and the safety case built around “no impact on the automation system” is quietly false. The discipline is absolute: the CPC reads nothing from the M+O domain except through VoR, and VoR writes only allow-listed, envelope-checked, approved values. Treat any subscription from control logic to a UNS topic as a defect.

Practical Recommendations

Start with a single unit, not the whole plant. Pick one asset with a clear reliability payoff — a critical pump, a compressor, a fired heater — and build the full second channel for just that asset. A working diode-to-analytics path on one unit is worth more than a paper architecture for all of them, and it gives the management-of-change reviewers something concrete and contained to assess.

Enforce read-only empirically, not by assumption. The uawrite probe from Step 1 belongs in your commissioning checklist and, ideally, in a periodic automated test. A diode you have not tried to write to is a diode you have not verified.

Keep VoR narrow on purpose. Resist requests to expand the allow-list; every tag added is a new thing that can be driven from the open domain. If an optimizer needs many writable points, that is a signal the use case may belong in the CPC, not across the diode.

A short checklist to stand up your first NOA second channel:

  • [ ] Choose one asset and enumerate the exact tags the M+O domain needs.
  • [ ] Configure a read-only OPC UA endpoint (encryption on, anonymous off, CurrentRead only).
  • [ ] Probe it with uawrite and confirm BadNotWritable.
  • [ ] Deploy a subscribe-only collector with no write path in code.
  • [ ] Publish into a typed UNS topic hierarchy with quality and timestamp

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *