Skip to main content
From v0.32.0 the SDK has a C# binding for .NET 8 desktop and server hosts, in preview. It drives the native library through the C API with P/Invoke, runs every engine call on one dedicated thread, and gives you an async ProtocolEngine with typed configuration, storage providers, events, telemetry and transport callbacks. Every engine method has a typed wrapper generated from the SDK’s interface definition, so a new method or configuration field reaches C# when the binding is regenerated.

Install

The binding ships as source in bindings/csharp and is not on NuGet. Check out the release and add a project reference:
It needs the .NET 8 SDK or later. Take the native library for your platform from the v0.32.0 GitHub release: The SDK loads the library from the directory in OFFLINE_PROTOCOL_LIBRARY_DIR when it is set, and otherwise from the application directory and the system paths. Take the source and the library from the same release.

Quick start

Method names, parameters and results follow the local API: parameters are JSON objects and results and events are JSON text. Service and document calls are on engine.Services and engine.Data, for example service subscriptions:

Storage

InitializeMlsWithFileStoresAsync uses the built-in file stores, sealed under a 32-byte store key, given in base64, that you keep out of the source. For your own backend, implement IProtocolStorage, wrap it in a NativeStorageProvider and pass two providers to InitializeMlsAsync. Prove a backend first with ProtocolEngine.RunStorageConformanceAsync. A provider is single use: to probe a backend and then use it, wrap the same IProtocolStorage in a new provider. MemoryProtocolStorage is an in-memory backend for tests.

Threads, events and callbacks

  • One engine thread. Every call for an engine runs on its own .NET thread behind a bounded queue (512 calls by default). Await each call; a slow call such as StartAsync delays every call queued behind it. Cancelling a call ends your wait, not a call already queued.
  • Events off the engine thread. SetEventCallbackAsync delivers each event as JSON on a dedicated delivery thread. A handler that throws is counted in EventHandlerFailureCount and never ends the process. PollEventAsync reads the same events without a callback. Keep handlers short and never call the same engine synchronously from one.
  • Transport callbacks. SetBleTransportCallbackAsync, SetWifiDirectTransportCallbackAsync, SetReticulumTransportCallbackAsync and SetNostrTransportCallbackAsync signal that a carrier has outbound frames. Drain them with the generated methods, such as BleGetNextFragmentAsync, until the queue answers null. Install them before StartAsync.
  • Dispose before exit. WinUI, WPF and MAUI hosts call DisposeAsync before the application shuts down, so telemetry flushes and transports stop cleanly.

What your host provides

  • Transports. The radios and sockets, through the transport callbacks and the generated transport methods.
  • Storage. The built-in file stores or two providers, as above.
  • Telemetry lifecycle. If you enable telemetry with EnableTelemetryAsync, the library fills os and the binding fills os_major. Call NotifyAppStateAsync when a desktop app leaves and returns to the foreground.

Reference

  • C# binding README: building, testing and the consumer check.
  • C# binding contract: the engine thread, events, storage providers, telemetry, typed configuration and transport callbacks.
  • C binding: the C API the binding calls.
The SDK is AGPL-3.0-only with a commercial license available; see licensing.