Files
UA-.NETStandard/samples/Gds/README.md
Marc Schier b97fd8d4b5 Fix OPC 10000-21 onboarding interoperability (#4385)
# Description

Fix the four blockers that prevented the OPC 10000-21 onboarding
registrar and client from working end to end:

- generate every `ModelDesign` target independently, including multiple
models in one directory, while preserving stable identifier CSV matching
and cross-model dependencies;
- ship the generated `Opc.Ua.Onboarding` constants, node states,
encodeables, and typed clients from `Opc.Ua.Gds.Common`;
- correct the standard `EncodedTicket` datatype to derive from
`ByteString`;
- align the new 2.0 `OnboardingClient` API with the generated
`ArrayOf<ByteString>` / `ArrayOf<StatusCode>` contract;
- make `BindToTicketStore` fully asynchronous, cancellation-aware, and
assign the declared output slot instead of appending a second output;
- add generated-model, client fallback, server binding, and in-process
end-to-end regressions.

The change also adds `samples/Gds/OnboardingRegistrar`,
`samples/Gds/OnboardingClient`, and `run-onboarding-demo.ps1`. The demo
loads the generated GDS and Onboarding models, restricts ticket
administration to an ephemeral `RegistrarAdmin` user, proves anonymous
access is denied, and completes a secure register/unregister round trip.

The History CI follow-up rejects negative and non-finite
processed-history intervals before provider dispatch and enforces the
buffered aggregate output cap while calculator results are flushed. This
prevents malformed requests from running until the client timeout,
exhausting memory, delaying server teardown, and causing later
alarm-event tests to fail.

## Related Issues

- Fixes #4378
- Unblocks OPCFoundation/UA-.NETStandard-Samples#819
- Tracks the canonical model correction in OPCFoundation/UA-Nodeset#122

## Validation

- Full `net10.0` test projects:
  - `Opc.Ua.SourceGeneration.Core.Tests`: 3803 passed, 8 skipped
  - `Opc.Ua.SourceGeneration.Tests`: 143 passed
  - `Opc.Ua.Gds.Tests`: 1114 passed, 42 skipped
- `Opc.Ua.History.Tests` with the CI coverage settings: 506 passed, 27
skipped
- `HistorianDispatcher` tests: 88 passed on `net10.0`.
- Invalid processed-history interval regressions passed on `net10.0` and
`net48`.
- Focused onboarding/source-generation regressions passed on `net48`.
- `dotnet build UA.slnx -p:CustomTestTarget=net10.0 --no-restore`
succeeded; the reported analyzer warnings are pre-existing in unchanged
XRegistry/WoT files.
- Packed all three GDS packages and verified the packaged
`Opc.Ua.Gds.Common` assembly contains the generated Onboarding model
surface.
- `pwsh samples/Gds/run-onboarding-demo.ps1` verified anonymous denial
and the authenticated secure ticket lifecycle.

## Checklist

- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [x] I have addressed **all** PR feedback received.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2d2c52c7-7040-4bd6-98de-aaf82ba7846b
2026-09-02 11:39:19 +02:00

3.4 KiB

OPC 10000-21 Onboarding Demo

This sample demonstrates the registrar-administration part of OPC 10000-21 end to end over a real OPC UA connection. It uses only the model and runtime APIs shipped by this repository:

  • OnboardingRegistrar loads the generated Opc.Ua.Onboarding model, exposes the standard DeviceRegistrar_Administration object, and binds RegisterTickets / UnregisterTickets to an injected MemoryTicketStore.
  • OnboardingClient connects with a managed session, resolves the well-known registrar NodeId, and uses the DI-provided Opc.Ua.Gds.Client.OnboardingClient.
  • run-onboarding-demo.ps1 builds both applications, waits until the OPC UA server reaches its running state, proves an anonymous caller is denied, runs the authenticated client, and stops the exact registrar process in a finally block.

The demo covers ticket administration. It does not implement the device-facing ProvideIdentities flow or a complete production GDS.

Run the complete demo

From the repository root:

pwsh samples/Gds/run-onboarding-demo.ps1

The script uses opc.tcp://localhost:62560/OnboardingRegistrar by default. Select a different port with:

pwsh samples/Gds/run-onboarding-demo.ps1 -Port 62660

Successful output ends with:

REGISTER Good Good
UNREGISTER Good
UNREGISTER_AGAIN BadNotFound
ONBOARDING_DEMO_OK

Pass -Keep to retain the temporary PKI stores and registrar logs for diagnostics.

Run the applications manually

Start the registrar:

dotnet run --project samples/Gds/OnboardingRegistrar -f net10.0 -- `
  --port 62560 `
  --pkiRoot "$env:TEMP\opcua-onboarding-registrar"

After ONBOARDING_REGISTRAR_READY is printed, run the client:

dotnet run --project samples/Gds/OnboardingClient -f net10.0 -- `
  --endpoint "opc.tcp://localhost:62560/OnboardingRegistrar" `
  --pkiRoot "$env:TEMP\opcua-onboarding-client"

Wire contract

The client API uses the Part 21 types directly:

ArrayOf<StatusCode> results = await onboarding.RegisterTicketsAsync(
[
    new ByteString(firstTicket),
    new ByteString(secondTicket)
]);

EncodedTicket is a subtype of ByteString. The server returns one StatusCode for every supplied ticket.

Security

The demo selects SignAndEncrypt with Basic256Sha256. For a self-contained local run, both applications automatically accept the other application's newly created certificate and store it under an isolated temporary PKI root. The script also generates a one-run random username and password, grants that user the generated RegistrarAdmin role, and restricts the two ticket methods to that role. The endpoint also permits an anonymous bootstrap session because ManagedSession activates its configured identity provider after creating the session; anonymous users have no permission to call the registrar methods.

Automatic certificate acceptance is a sample-only convenience. A production registrar must use managed trust lists, authenticate and authorize registrar administrators, persist tickets in a protected store, and apply the audit/redaction requirements described in the GDS developer guide.

For a manual run, set ONBOARDING_DEMO_USER and ONBOARDING_DEMO_PASSWORD to the same non-empty values in both terminals before starting either application. Do not reuse demo credentials in a production deployment.