What's New in OPC UA .NET Standard 2.0

August 9, 2026 · View on GitHub

This document is a developer-facing tour of the changes between 1.5.378 and 2.0. It is organised by theme and layer; each section describes the broad-stroke change with a paragraph and links to the deeper feature documentation in this folder.

If you are migrating an existing application, the companion Migration Guide is the prescriptive, API-level reference.

At a glance

  • The OPC UA built-in types are now allocation-friendly value types, with a Variant-first public API, a new ByteString type, and ArrayOf<T> / MatrixOf<T> replacing untyped array shapes.
  • Server- and client-side stacks are now fully async/await with cancellation flowing through the request lifetime, TimeProvider everywhere, and the new AsyncCustomNodeManager powering all built-in NodeManagers.
  • A first-class hosting story: services.AddOpcUa() and IOpcUaBuilder plug servers and clients into Microsoft.Extensions.DependencyInjection and the .NET Generic Host, complemented by a fluent server and ManagedSession fluent client builder.
  • Native AOT support across the stack, including AOT-clean source generators, NodeSet export/import, and reference servers/clients.
  • New companion-spec coverage: Part 9 (Alarms & Conditions), Part 11 (Historical Access) + Part 13 (Aggregates), Part 16 (State Machines), Part 17 (Alias Names), Part 18 (Role Management), Part 20 (File Transfer), Part 100 (Device Integration), plus OPC 10100-1 WoT Connectivity and a Local Discovery Server.
  • Source generators emit NodeManagers, typed ObjectType proxies, and IEncodeable data types from model design XML, removing hand-written boilerplate while staying AOT-clean.
  • GDS is now Part 12 full-compliance, with arbitrary certificate groups, custom group support, and modernised Push/Pull APIs.
  • OPC UA Part 4 §6.6 redundancy plus opt-in distributed high availability. Server and client redundancy (Server.ServerRedundancy / ServiceLevel, ManagedSession.WithServerRedundancy()) ship in the box, and new Opc.Ua.Redundancy(.Server/.Client/.Kubernetes) packages add distributed address-space / session / subscription mirroring over an in-package CRDT (eventual) or Raft (strong) shared store, with a Kubernetes deployment guide.
  • An MCP server ships in the box so an LLM/Copilot can drive an OPC UA client; and a 2.0 Migration Analyzer + code fixer automates the mechanical parts of upgrading to v2.

Breaking changes at a glance

2.0 is the first major break of the public API since the project moved to .NET Standard. The biggest sources of breakage are the readonly-struct built-ins (NodeId, ExtensionObject, Variant, DataValue, QualifiedName, LocalizedText, ArrayOf<T>), the Variant-for-object pivot in code and API, the new IEncodeableFactoryBuilder and IType hierarchy, the removal of Newtonsoft.Json from Opc.Ua.Core, the ManagedSession family next to the classic Session, and the move to AsyncCustomNodeManager for server extensibility. The Migration Guide walks every break in detail, with the companion 2.0 Migration Analyzer handling most of the mechanical edits automatically.

Cross-cutting themes

Type system and immutability

The built-in OPC UA types have been redesigned around immutability and allocation-light value semantics. NodeId, ExpandedNodeId, QualifiedName, LocalizedText, Variant, ExtensionObject, DataValue, and ArrayOf<T> are now readonly structs; null checks are replaced with IsNull / .Null to align with INullable. A new ByteString is preferred over byte[] in public API, and ArrayOf<T> / MatrixOf<T> provide first-class array and matrix abstractions. The object-typed public surface has been replaced with Variant across the stack and in source-generated code, eliminating boxing in encoders, decoders, and node-state read/write paths. Equality on ExtensionObject, StatusCode, and numeric ranges has been tightened, and DateTimeUtc makes timestamp intent explicit. See the 2.0 migration guide — Improved Type Safety section for the per-type deltas.

Async, cancellation, and TimeProvider

The server now runs fully on the Task-based asynchronous pattern. The new AsyncCustomNodeManager is the canonical base class for NodeManagers and threads CancellationToken through every service call; built-in managers (CoreNodeManager, DiagnosticsNodeManager, ReferenceNodeManager) have all migrated to it, and MonitoredNode is IAsyncNodeManager-aware. A new server-side RequestLifetime propagates the request-scoped cancellation token through the call stack, so a client-cancelled request short-circuits cleanly. Synchronous-over-async patterns have been removed from non-obsolete public APIs. The stack also adopts System.TimeProvider for all timing primitives, replacing direct DateTime.UtcNow and Timer use; this is what makes server logic deterministic under test and tolerant of system-clock changes.

Dependency injection and hosting

The stack now offers a unified Microsoft.Extensions.DependencyInjection surface: a single services.AddOpcUa() returns an IOpcUaBuilder, and every feature library hangs its own .AddXxx(...) extension off it. Servers run as IHostedServices under the .NET Generic Host; options bind from Action<T> or IConfiguration; identity providers, certificate manager, secret store, file system, historian, alarms, and the GDS extensions all register through the same builder. Alongside DI, a source-generated fluent server API lets applications stand up a server from a model design XML with a few .AddXxx().WithYyy() calls; the ManagedSession fluent builder is the equivalent on the client.

Native AOT

The full stack — Core, Types, Client, Server, ComplexTypes, GDS, PubSub, and the source generators — is now Native AOT friendly. Public API avoids reflection paths that require trimming suppression, the source generators emit AOT-clean code, and the reference servers/clients can be published as self-contained single-file native binaries. UANodeSet import/export and the encodeable factory have been reworked to function under AOT, and the test matrix includes AOT smoke tests for each shipped library.

Source generators and modeling

The new source-generation pipeline emits the typical OPC UA boilerplate from model design XML rather than hand-written code. The NodeManager generator produces a fully async, fluent NodeManager skeleton plus typed *State properties for every node; the DataType generator emits IEncodeable implementations from POCO classes; and a new generator emits **typed method proxies on ObjectType**s so callers invoke methods with a strongly-typed signature rather than a generic Call plus variant arrays. The encodeable factory build path uses an IEncodeableFactoryBuilder so factories can be assembled deterministically and AOT-cleanly; OPC UA OptionSet data types are now backed by generated structures, and cross-assembly model references are tracked via the ModelDependencyAttribute. The generator can also default a model's instance modelling rules to its type-definition rules, which the stack now opts into. For Optional Variable/Method children the generator emits five chainable Add{Child} overloads per typed state class (idempotent ensure-child, Action<TChild> configure, conditional configure, Func<TChild, TChild> replace, conditional replace) so opt-in extensions of singleton instances collapse into a single fluent chain.

OPC UA companion-spec coverage

This release substantially extends companion-spec coverage with full server- and client-side implementations:

  • Part 9 — Alarms and Conditions: full server + client implementation with latched / silenced / out-of-service variants, alarm groups and a suppression engine, rate metrics, a typed AlarmClient, the AlarmEventFilterBuilder, and IAsyncEnumerable alarm streaming. See Alarms and Conditions.
  • Part 11 — Historical Access + Part 13 — Aggregates: a provider model with an in-memory historian and a HistoryClient for raw, modified, at-time, processed, and annotation reads/updates. All 37 standard v1.05.07 aggregate functions, with native push-down where available and a framework fallback otherwise. See Historical Access and Aggregates.
  • Part 16 — State Machines: a unified fluent StateMachineBuilder with both definition (FluentFiniteStateMachineState) and lifecycle (attach behaviour to stack-shipped or generator-emitted FSMs) modes, plus client-side streaming / read helpers on the generated *TypeClient proxies.
  • Part 17 — Alias Names: full server + client support for AliasNameType, AliasNameCategoryType, FindAlias, FindAliasVerbose, AddAliasesToCategory, DeleteAliasesFromCategory, and LastChange. See Alias Names.
  • Part 18 — Role Management: full server-side role administration surface, with the server automatically assigning the OPC UA Part 3 §4.9 TrustedApplication role, and a pluggable identity-provider model that supports anonymous, username, X.509, and token-issuer flows (OAuth2 / OIDC / Entra / JWT).
  • Part 20 — File Transfer: a server-side FileSystem library, with a matching System.IO-style FileSystemClient on the client.
  • Part 100 — Device Integration: the Opc.Ua.Di / Opc.Ua.Di.Server / Opc.Ua.Di.Client library trio with a fluent IDeviceBuilder, device sub-type extensions, lock service, software-update package store, and client helpers. See Device Integration and Software Update.
  • Parts 210 and 211 — Relative Spatial Location and Global Positioning: source-generated released RSL/GPOS models, standalone and composed server hosting, technology-neutral position providers, typed clients and streams, frame-chain resolution, WGS84/ENU conversion, and rigid/similarity/affine Zone fitting. The robot/OpenUSD sample publishes independently configurable mobile robot poses. See Positioning.
  • OPC 40010-1 — Robotics (over OPC 40001-1 — Industrial Automation): the Opc.Ua.Robotics / Opc.Ua.Robotics.Server / Opc.Ua.Robotics.Client library trio over Device Integration, with source-generated Robotics 1.02 and IA models, AddRobotics / ConfigureRobotics hosting, ordered model providers, and validated fluent topology builders that assemble motion device systems, controllers, motion devices, axes, power trains, motors, gears, drives, safety states, and task controls with the correct companion-spec references. See Robotics, including the draft Robot Intent task-level command model.
  • OPC 10100-1 — WoT Connectivity: model, server, and client libraries for surfacing OPC UA servers as Web of Things Thing Descriptions, with the WoTAssetConnectionManagement server methods gated by a configurable WotManagementAccessPolicy (defaults: SignAndEncrypt channel + SecurityAdmin role + no anonymous). See WoT Connectivity.
  • Local Discovery Server: a built-in LDS implementation usable standalone or as part of a hosted server.

Other server-side feature work: NodeManagement service set (AddNodes, DeleteNodes, AddReferences, DeleteReferences with an INodeManagementAsyncNodeManager opt-in and a per-manager AllowNodeManagement gate), Model Change Tracking (server-side ModelChangeAggregator and auto-emitted GeneralModelChangeEvents, with client-side per-node INodeCache.InvalidateNode), and the Subscriptions and Monitored Items service set (V2 subscription engine, declarative + imperative SetTriggering with N:M support, and IAsyncEnumerable-based streaming subscriptions for state-machine waits and short-lived monitoring).

Performance, memory, and pooling

The type-system rework eliminates a large class of allocations: every encode/decode of NodeId, Variant, DataValue, ExtensionObject, and their collections now stays on the stack or in pooled buffers. A new IPooledEncodeable activator pool further reduces GC pressure on hot encode paths. Server-side, MonitoredNode2 caches role-permission validation event-driven (no more per-publish recomputation), publishing queues use channel-based consumers, and NodeState.ReadAttributes has been optimised. Lifetime bugs that surfaced under load were fixed in several places: socket and event-handler leaks during server restart, timer leaks in ChannelAsyncOperation.EndAsync, TcpTransportListener resource leakage in ServerBase.StopAsync, undeleted subscription diagnostic nodes, and the abandoned-subscription map migrated from a locked List to a ConcurrentDictionary.

Security and certificates

A new ref-counted Certificate wrapper and the CertificateManager segregated-interface design replace the older X509Certificate2 exposure: certificates are tracked deterministically, shared safely, and disposed predictably across stores, channels, and identity flows. Secure-channel negotiation has been hardened; the client now auto-detects and force-renegotiates on server certificate rotation, and application-certificate lookup can fall back from a concrete ApplicationCertificateType to the abstract type when no concrete entry matches. The EncryptedSecret machinery now supports both RSA and ECC and is bridge-compatible with legacy .NET / OPC UA implementations. The server ships with client lockout for failed authentication attempts, and SubCA revocation no longer auto-creates an empty CRL on the issuing CA.

Cryptography is now pluggable. A CryptoProvider model lets an application route cryptographic operations to another library, an offboard service or hardware, chosen per purpose and per security policy and injected through AddCryptoProvider(…). The default is unchanged and costs nothing: everything resolves to platform cryptography until something is bound. Private keys no longer have to be owned by the certificate — a key can be held detached, in a TPM, a smart card or an HSM, and never enter process memory. Certificate.CopyWithDetachedPrivateKey is the seam that makes this work on every platform, which X509Certificate2.CopyWithPrivateKey does not. The optional OPCFoundation.NetStandard.Opc.Ua.Security.Pkcs11 package adds a PKCS#11 certificate store addressed by RFC 7512 pkcs11: URIs. Which cryptographic module performed an operation, and whether it carries any validation, is auditable through logs, metrics and the address space, and can be constrained with a compliance policy.

By layer

Server

AsyncCustomNodeManager is now the recommended base for all custom NodeManagers, and every NodeManager shipped with the stack has migrated to it. MonitoredNode is IAsyncNodeManager-aware and uses channel-based queuing under load; events from the server node are dispatched through multiple channel consumers; role-permission validation is cached event-driven in MonitoredNode2; and the request queue, event handling, and publishing path have all been hardened with new test coverage. Server identity is now pluggable end-to-end (anonymous / username / X.509 / token issuer) and persistent users are loaded through IUserDatabase.GetUsers. Per OPC UA Part 3 §4.9 the server assigns the TrustedApplication role automatically. The NodeManagement service set, per-NodeManager Allow… gates, and ModelChangeAggregator round out the server extensibility surface. The server, the audit/redaction APIs, and the publishing path have been audited for sync-over-async and converted to TAP. See Sessions for the matching session-/subscription-engine story.

Client

The client now offers two coexisting paths. The classic Session remains for callers that own session lifecycle. The new ManagedSession encapsulates the connection state machine, reconnect policy, and pluggable subscription engine behind a fluent builder; it is the recommended path for new code. The V2 subscription engine runs alongside the classic engine and has feature and test parity with it; an opt-in SubscriptionRecoveryPolicy lets servers signal Good_SubscriptionTransferred without surprising the client; and the user token policy used on Connect is now re-used during Reconnect / ReactivateSession. Under the hood, a new IClientChannelManager owns client-side transport channels with reference counting, sharing, and coalesced reconnect: sessions, discovery clients, and registration clients targeting the same ConfiguredEndpoint (with the same reverse-connect identity) share a single underlying ITransportChannel, reconnect is transparent to callers, and the previous AttachChannel / DetachChannel + SessionReconnectHandler patterns are obsoleted. A shared IRetryBudget collapses the two-level retry deadline so reconnect no longer compounds multiplicatively. The new unbounded monitored items feature transparently shards a V2 ManagedSession subscription across multiple real server-side Subscription partitions when the server's MaxMonitoredItemsPerSubscription cap is hit — callers that exceed the cap continue to succeed instead of failing with BadTooManyMonitoredItems, and the public ISubscription / MonitoredItemCollection shape is unchanged. New client-side features include FileSystemClient (a System.IO-style async client over OPC UA File methods), HistoryClient, AlarmClient, and source-generated typed ObjectType proxies. Client-side NodeSet export extracts a server's address space to NodeSet2 XML, and ModelChangeTracking keeps the local INodeCache consistent with server-side model changes.

High availability and redundancy

2.0 maps OPC UA Part 4 §6.6 redundancy across the server, client, and network, and layers an opt-in distributed high-availability story on top. On the server, AddServerRedundancy(...) publishes the Server.ServerRedundancy nodes, drives Server.ServiceLevel, advertises the NTRS non-transparent discovery capability, and exposes RequestServerStateChange for administrator-driven Maintenance/NoData failover — for every RedundancySupport mode (None/Cold/Warm/Hot/HotAndMirrored/Transparent). On the client, a single ManagedSession with WithServerRedundancy() reads that metadata and fails over transparently, so the same code works whether or not the server is configured for redundancy.

The new Opc.Ua.Redundancy, Opc.Ua.Redundancy.Server, Opc.Ua.Redundancy.Client, and Opc.Ua.Redundancy.Kubernetes packages add the distributed building blocks behind DI/fluent seams. UseDistributedAddressSpace / UseReplicatedAddressSpace, UseDistributedSessions / UseReplicatedSessions, and UseDistributedSubscriptionMirroring mirror address-space topology and values, session state (fast reconnect that still runs a full ActivateSession signature re-check against a single-use nonce), and subscription / retransmission state across replicas. UseRedundancyConsistency selects the shared store's consistency model — a leaderless CRDT gossip layer (eventual, active/active) or a linearizable Raft layer (DefaultRaftConsensus, strong, for single-use nonces and leader election) — both over the in-package NanoMsg transport, with every record authenticated-encrypted through IRecordProtector. Address-space hydration uses a snapshot + delta-log fast path for quick time-to-ready on failover, and an optional GetEndpoints load-direction seam (UseServerLoadDirection) can steer clients to the best replica. The default path (no store configured) is unchanged and zero-overhead. See High Availability for the full design and Kubernetes for the deployment guide.

Global Discovery Server

The GDS implementation is now full OPC UA Part 12 compliance, including modern StartRequestToken / FinishRequestToken flows (AuthorizationService) and the pull/push KeyCredentialService. The client supports pushing to arbitrary certificate groups; the server supports custom certificate groups; SubCAs can be revoked without auto-creating an empty CRL; and method-call validation is strict. The full developer guide is in GDS.

Part 14 PubSub modernization

The PubSub stack (Opc.Ua.PubSub, Opc.Ua.PubSub.Udp, Opc.Ua.PubSub.Mqtt, Opc.Ua.PubSub.Server) is rewritten end-to-end to track Part 14 v1.05.06.

  • Native AOT clean. The combined reference sample (ConsoleReferencePubSubClient, with publisher / subscriber / external modes) publishes AOT with zero IL2026 / IL3050; PubSubAotTests exercises every runtime path under AOT.
  • DI-integrated. services.AddOpcUa().AddPubSub(o => …) registers the runtime, scheduler, security subsystem, transports, and SKS into the standard IServiceCollection. See DependencyInjection.md. The previous "PubSub is not part of the dependency-injection surface" caveat is removed.
  • Fluent builder. PubSubApplicationBuilder composes connections, groups, writers, readers, transports, and security in code; XML configuration loads through the same builder. Inline construction or full IPubSubConfigurationStore round-tripping are equivalent.
  • Full v1.05.06 spec coverage. UADP (§7.2.4) and JSON (§7.2.5) encoders/decoders, including JsonEncodingMode { Verbose, Compact, RawData }, SingleNetworkMessage, Action and Discovery messages; UDP datagram-v2 (DatagramConnectionTransport2DataType + QosCategory → DSCP), MQTT 3.1.1 + 5.0 with QoS 0/1/2 and retained metadata at startup; SKS pull / push (§8.5.1, §8.5.2); AES-128-CTR and AES-256-CTR with HMAC-SHA-256 (NIST SP 800-38A F.5.1 / F.5.5 KAT-asserted).
  • Per-component diagnostics. Every connection, group, writer, and reader now carries its own IPubSubDiagnostics instance, surfaced on the address space when AddPubSubAddressSpace is wired.
  • Runtime configuration mutation. IPubSubApplication.Add/Remove* methods compose new connections / groups / writers / readers without a stop-reconfigure-restart cycle; the same methods are bound to the Part 14 §9 PublishSubscribe Object methods.
  • Security wiring. UadpSecurityWrapper is now invoked at send / receive; configurations that named a SecurityMode other than None actually get that security applied.
  • Retained metadata. MetaDataPublisher advertises every active DataSetMetaData once at startup (UDP discovery / MQTT retained topic) so subscribers that join mid-stream can decode RawData.
  • MQTT 3.1.1 + 5.0. The MQTT transport uses MQTTnet v4 on net48 / netstandard and v5 on net8 / 9 / 10. Certificate authentication per Part 14 §6.4.2.2.4 is supported.

For library reference and code samples, read PubSub.md. For the upgrade story (breaking changes, compatibility matrix, and codemod recipes), read migrate/2.0.x/pubsub.md.

Tooling

A new MCP server (see MCP Server) exposes OPC UA client operations as Model Context Protocol tools, so an LLM or Copilot can browse, read, write, subscribe, and call methods on any OPC UA server. The MCP server additionally ships packet-capture, packet-decode, and packet-replay tools so the agent can capture a UA-TCP exchange to PCAP, inspect the decoded service calls, and replay them — useful for reproducing interop issues without re-running the original client. A 2.0 Migration Analyzer + code fixer ships as a Roslyn analyser package (OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer) that detects the typical 1.5.378 → 2.0 patterns and applies most of the mechanical edits automatically; see the Migration Guide for the opt-in workflow.

Build, CI, and observability

The build pipeline now runs on a managed DevOps pool with a per-TFM build/test matrix and parallelized client tests. Nullable is enabled across src/, the nine src/, and the samples/ projects; dispose-analyzers (CA2000, CA2213) are on and clean. The repository follows a strict dotnet format baseline (whitespace, IDE, RCS) enforced by the opc-ua-codestyle-enforcer agent. Code analysis runs at "preview" level with "all" mode, package validation is on, and treat-warnings-as- errors is set repo-wide. On the runtime side, Diagnostics is plumbed through ITelemetryContext: loggers, meters, and activities all hang off the same context object, and log redaction is wired through the audit APIs. Tests have been reorganised for faster CI, with several integration suites separated from unit suites, and code-coverage gates apply to all non-test, non-application projects. Those gates now run inside the pipeline itself - an absolute project floor plus a changed-lines check - instead of relying on an external coverage service; see Continuous integration.

Further reading