← Browse
@opcfoundation/ua-netstandard
OPC UA .NET Standard Stack - GitHub Copilot Instructions
instructionscopilot
Install
agr install @opcfoundation/ua-netstandard --target copilotWrites 1 file into .github/copilot-instructions.md, pinned to git-bca924ee.
- .github/copilot-instructions.md
Document
OPC UA .NET Standard Stack - GitHub Copilot Instructions
Project Overview
This is the official OPC UA .NET Standard Stack from the OPC Foundation. It provides a reference implementation of OPC UA (Open Platform Communications Unified Architecture) targeting .NET Standard, allowing development of applications that run on multiple platforms including Windows, Linux, iOS, and Android.
Key Technologies
- SDK: .net 10 SDK
- Language: C# with LangVersion 14.0
- Target Frameworks: .NET Standard 2.0/2.1, .NET Framework 4.8, .NET 8.0 (LTS), .NET 9.0, .NET 10.0 (LTS)
- NativeAOT
- Project Type: Class libraries, console applications, and reference implementations
- Architecture: OPC UA Stack with Client, Server, Configuration, Complex Types, GDS, and PubSub components
Build and Development
Building the Project
- Prerequisites: .NET SDK 10.0
- Restore dependencies:
dotnet restore 'UA.slnx' - Build: Use Visual Studio 2026 or
dotnet build - Key solutions:
UA.slnx- Contains all projects
- Contributor guide: see
docs/DeveloperGuide.mdfor prerequisites, building (including per-TFM), testing, coding standards, and how-to recipes.
Project Structure
src/- Core OPC UA libraries (Client, Server, Configuration, etc.)samples/- Reference applications (ConsoleReferenceServer, etc.)tests/- Unit and integration teststools/- Source generators, analyzers, and the OPC UA MCP tooldocs/- Documentation filesfuzzing/- Fuzz targets, corpora, and support scripts
Build Properties
Directory.Build.props- Central build propertiesDirectory.Packages.props- Centralized package management- Analysis level is set to "preview" with the "all" analysis mode
- .NET analyzers are enabled
- Package validation is enabled
- .editorconfig is enforced during build. Fix all errors, warnings, and informational messages. Do not suppress without marking the suppression with a comment explaining why and a TODO to fix later.
Core Concepts to Understand
- Nodes and NodeManagers: Understand the node structure and how to implement custom node managers
- Sessions and Subscriptions: Properly manage client sessions and subscriptions (including durable subscriptions)
- Data Encoding: Use the binary encoding/decoding system for OPC UA data types
- Complex Types: Support for complex type definitions and their serialization
- Transport: UA-TCP and HTTPS transports with reverse connect capability
IMPORTANT RULES
- Rules apply to any new code added and existing code that is changed.
- All new code should use Async/await (TAP), APM and synchronous to Async are not allowed.
- DO NOT create SYNC over ASYNC (GetAwaiter().GetResult(), Wait(), Result) unless explicitly requested/confirmed.
- All types implementing
INullablemust never be used or added via theSystem.Nullable<T>(T?). Instead use.IsNullcheck on the type to determine whether it is null and the.Nullor default to create a null value. - ALWAYS use
TryGetorTryGetValue, or similar on struct types vs casting. Particularly NEVER use.AsBoxedValueof theVarianttype or.ValueofSystem.IUnion. - NEVER use
objectorobject?in public API unless overridingEquals. If OPC UA related API, useVariant. - Prefer ArrayOf over read-only collection types and ReadOnlyMemory — including in new public API surfaces (parameters, properties, return types). When adding new APIs in 2.0 (or modifying existing 2.0 APIs), use
ArrayOf<T>rather thanIReadOnlyList<T>/IList<T>/T[]. This applies even when the value originates from aIReadOnlyList<T>source — wrap it inArrayOf<T>at the API boundary. - Prefer ByteString over ArrayOf, byte[], ReadOnlyMemory and byte readonly collections in public API.
- Prefer Span/ReadOnlySpan over byte[] in any API.
- DO NOT use API that is marked as [Obsolete] unless used inside "Test code".
- DO NOT add new API that is not compatible with NativeAOT (needs suppression, annotation).
- All new functionality added must wire into the DI infrastructure and be injectable. Direct "create"/construct path should also be available as fallback.
- Consider making new functionality available using a fluent API (and ideally integrated into the existing API if it can extend it).
- Maintain compatibility with the previous version (1.5.378, master378 branch) in master, mark any replaced API with [Obsolete]. Already marked [Obsolete] API in 1.5.378 (master378) can be removed/replaced in master.
- Use modern C# and .net and add a polyfill to Opc.Ua.Types/Polyfills if API is not available on older platforms.
- Use new "extension" keyword when you need to define "static" or "property" extensions. Use old style extension methods for the rest.
- Pass
DataValuevalues by ref when possible (useinfor one way arguments). Addinginkeyword is not considered a breaking change since the API can be invoked with and without ref. - Support the latest version of the OPC UA specifications (1.05.07 for OPC UA core)
- NEVER expose "locks" or locking in any internal or public API surface because nobody can effectively reason over the lock behavior. NEVER expose a "lock" as protected field to sub classes.
- NEVER use
private readonly object m_lock = new()or any otherobject-typed sync root. All synchronous locks MUST useSystem.Threading.Lock(a polyfill inOpc.Ua.Types/Polyfills/System.Threading.csmakes it available on every supported TFM). Treatprivate readonly object m_lock = new()as an analyzer-level error. Apply this rule to every code path you add or modify; do not introduce new object-based locks even when imitating surrounding code.
Architecture and patterns
- DO NOT rely on inheritance to provide "extensibility". Instead - make all non abstract public classes sealed by default and ask for exceptions.
- Prefer a provider model with injectable providers, see FileSystem or HistoricAccess providers as examples.
- Align all architectural decisions around the following high level goals:
- Running servers with High availability and in a distributed system. Especially consider what this means for "state".
- A "simple" API with progressive "advanced" feature sets, example: Minimal* samples and the focus on fluent API and ManagedSession API.
- Use SOLID principles. Encapsulate concerns and do not let implementation bleed through the APIs (e.g. GOOD: reconnect handling in managed session, BAD: ReconnectHandler (old)).
- ALWAYS REUSE existing features and concepts (docs/*) inside new features, e.g.
- All source generated code, in particular ObjectType proxies should be used over manually calling service calls inside new clients.
- Consider using the source generators to implement emitting "boilerplate", especially if it is related to the OPC UA standard (e.g. information model).
- Base services: File System, Certificate manager, Secret store, State machine, Alarms and conditions Streaming subscription, Sessions, etc. in new code. (Documented in docs/*).
- Observability is plumbed through via
ITelemetryContext. Use it to create anILoggerfor logging; follow the source-generated logging conventions indocs/DeveloperGuide.md.
- If reuse is not possible, ASK whether to extend an existing feature so it becomes reuseable.
Code Style
- Add the OPC Foundation MIT license header to all source files.
- NEVER use #region/#endregion directives. Remove them when you encounter them.
- NEVER use comment-only "region" dividers (e.g.
// === Section ===,// --- Section ---). Remove them when you encounter them. - NEVER add
#nullable enableto source files when the containing project already sets<Nullable>enable</Nullable>in its.csproj(which most projects in this repo do). Remove the redundant directive when you encounter it. - ALWAYS add a line break after a statement ending with
; - ALWAYS Follow the
.editorconfigsettings strictly. Fix all warnings, errors and informational messages before proposing a fix.- Use 4 spaces for indentation in C# files
- Maximum line length: 120 characters
- End-of-line: CRLF
- Always insert final newline
- Trim trailing whitespace
- Use UTF-8 encoding
- Order of members in classes, struct, records: Constructor, Properties and non-private Events, Methods, Fields. Each ordered from public->protected->internal->private.
- Braces: Use Allman style (braces on new line for control blocks, types, and methods)
- Null-checking: Use throw expressions and conditional delegate calls when appropriate but at minimum in all public API
- Access modifiers: Always specify access modifiers explicitly.
- Code analysis: All code must pass Roslyn analyzers (Roslynator.Analyzers and Roslynator.Formatting.Analyzers)
- Warnings: Treat warnings as errors (
TreatWarningsAsErrorsis enabled)
- Follow standard C# naming conventions. Do not use underscores in method names.
- Assembly prefix:
Opc.Ua(Except applications, or if otherwise requested) - Package prefix:
OPCFoundation.NetStandard - Always use a line break after
<summary>and before</summary>for all members (except for documentation of fields). This applies to every XML-doc summary, including in sample/application code — never write a single-line/// <summary> ... </summary>; always put the text on its own line between the opening and closing tags. - Use source-generated
[LoggerMessage]logging; never callILogger.LogInformation/LogError/...directly. Follow the per-file<Class>Logand per-assembly<AssemblyToken>EventIdsconventions indocs/DeveloperGuide.md.
Security Requirements
- Never hardcode credentials, certificates, or secrets in source code
- Certificate Management: All certificates must be managed through the certificate store system (see
docs/CertificateManager.md) - Secrets: All secrets must be managed through the secret store system (see
docs/CertificateManager.md) - Security Profiles: do not use hash algorithms other than SHA2 or higher.
- Authentication: Properly implement anonymous, username, and X.509 certificate user authentication
- Audit and Redaction: Use the audit and redaction APIs for sensitive information
Testing Standards
- Use NUnit or TUNIT framework (match existing test projects)
- DO NOT mix Nunit and TUNIT in the same project.
- DO Use NUnit asserts methods (Assert.That). Use the TUNIT assertions when using TUNIT.
- DO Use Moq for mocking in NUnit tests. Use the TUNIT mock libraries for TUNIT tests.
- DO NOT USE the classic Nunit asserts (E.g. Assert.AreEquals) or other libraries.
- All new features must include unit tests. Tests should be simple and cover positive and negative scenarios. Unit tests go into the corresponding .Tests project
- DO Maintain or improve code coverage
- All projects should have at least 80% coverage (except sample and test projects).
- All client/server as well as pub/sub features must also have Integration tests. Integration tests go into integration projects <component/area>.Tests.
- Tests must be deterministic and pass in CI/CD environment
- Code coverage is monitored via Coverlet and MUST NOT decrease
- Run all tests with
dotnet testfrom solution root on UA.slnx - When updating tests fix above for the test only.
- Mirror the structure of the code being tested
- Use descriptive test method names that explain what is being tested
- DO NOT use _ in test method names; use PascalCase
- Ensure that any null assertions do not assert null of a "struct" type. If structs implement
INullabletest for IsNull to be true or false.
Documentation
When to Update Documentation
- Add a new doc in
docs/when adding new features and link from/docs/README.md - Review all other docs for consistency and when needed, link the new doc from other docs.
- When manual migration from 1.5.378 (master378) is required or code was marked [Obsolete], update migrationguide.md
- Update
/README.mdfor significant changes - Keep
NugetREADME.mdupdated for package-related changes - Document breaking changes prominently
- Keep
docs/DeveloperGuide.md(the contributor onboarding guide) current when build, test, or coding-convention changes affect contributors.
Documentation Style
- Use clear, technical language
- Include code examples where appropriate. For new Client side features, always add code examples to show how to use the API and explain the developer experience.
- Reference OPC UA specifications when relevant
- Maintain consistent markdown formatting
Dependencies and Package Management
- Use centralized package management (Directory.Packages.props)
- Audit packages for security vulnerabilities (NuGetAudit is enabled)
- Only add necessary dependencies and ask for approval for new dependencies. Most functionality is implemented within the stack itself so check the repo first.
- Prefer stable, well-maintained packages.
- Do not use packages with incompatible license (e.g. GPL, AGPL or commercial)
- Check compatibility with all target frameworks
- Prefer AOT and trimmable annotated nuget packages over others.
- If a nuget dependencies is not explicitly marked as Native AOT compatible, add AOT tests to the AoT test project to exercise all code paths using the dependency.
Contributing
Before Submitting Changes
- Ensure all tests pass
- Run code analysis and fix all warnings
- Follow the existing code structure and patterns
- Check that your changes don't break compatibility
- Review security implications of your changes
- Contributors must agree to the OPC Foundation CLA
Pull Request Guidelines
- Provide clear description of changes
- Reference any related issues
- Ensure that net48 and net10.0 TFM tests in UA.slnx solution run successfully before creating the PR.
- Ensure CI/CD pipelines pass
- Use the opc-ua-codestyle-enforcer agent if needed to ensure compliance before opening pull requests.
Common Tasks
Adding New OPC UA Functionality
- Review OPC UA specification for the feature using the online reference or appropriate tools available (e.g. MCP).
- Implement in appropriate library/libraries (add new ones for new companion spec, or find the appropriate place in an existing one).
- Add comprehensive tests (see test requirements)
- Update documentation (see documentation requirements)
- Consider backward compatibility (see earlier)
Certificate Management
- NEVER commit certificates or secrets of any kind to the repository
- Use the certificate manager APIs
- Follow guidelines in
docs/Certificates.md - Test with different certificate configurations
Performance Considerations
- Be mindful of memory allocations in hot paths
- Use async/await properly to avoid blocking
- Consider thread safety
- Profile performance-critical code
- Avoid locking where possible.
- If needed use SemaphoreSlim to lock instead of lock keyword
- If a synchronous lock is required/desired, the field type MUST be
System.Threading.Lock(a polyfill is provided inOpc.Ua.Types/Polyfills/System.Threading.cs). NEVER declare a sync root asprivate readonly object m_lock = new()— useprivate readonly System.Threading.Lock m_lock = new()instead. This rule applies to every file in the repository.
Resources
- OPC UA Specification: https://reference.opcfoundation.org/
- Documentation: See
docs/directory - Samples Repository: https://github.com/OPCFoundation/UA-.NETStandard-Samples
- NuGet Packages:
- Types: https://www.nuget.org/packages/OPCFoundation.NetStandard.Opc.Ua.Types/
- Core.Types: https://www.nuget.org/packages/OPCFoundation.NetStandard.Opc.Ua.Core.Types/
- Core: https://www.nuget.org/packages/OPCFoundation.NetStandard.Opc.Ua.Core/
- Client: https://www.nuget.org/packages/OPCFoundation.NetStandard.Opc.Ua.Client/
- Server: https://www.nuget.org/packages/OPCFoundation.NetStandard.Opc.Ua.Server/
- Preview Feed: https://opcfoundation.visualstudio.com/opcua-netstandard/_packaging?_a=feed&feed=opcua-preview%40Local
Trust
Not scanned yet. Artifacts are graded after they are crawled, so a recently discovered one may have no result for a while.
Versions
git-bca924eeff822026-08-04