Cirreum.RemoteConnections.WebSockets 2.0.2

dotnet add package Cirreum.RemoteConnections.WebSockets --version 2.0.2
                    
NuGet\Install-Package Cirreum.RemoteConnections.WebSockets -Version 2.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Cirreum.RemoteConnections.WebSockets" Version="2.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Cirreum.RemoteConnections.WebSockets" Version="2.0.2" />
                    
Directory.Packages.props
<PackageReference Include="Cirreum.RemoteConnections.WebSockets" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Cirreum.RemoteConnections.WebSockets --version 2.0.2
                    
#r "nuget: Cirreum.RemoteConnections.WebSockets, 2.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Cirreum.RemoteConnections.WebSockets@2.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Cirreum.RemoteConnections.WebSockets&version=2.0.2
                    
Install as a Cake Addin
#tool nuget:?package=Cirreum.RemoteConnections.WebSockets&version=2.0.2
                    
Install as a Cake Tool

Cirreum.RemoteConnections.WebSockets

NuGet Version NuGet Downloads GitHub Release License .NET

Raw WebSocket transport for long-lived Cirreum client connections

Overview

Cirreum.RemoteConnections.WebSockets is the raw WebSocket implementation of Cirreum's IRemoteConnection abstraction — a typed, lifecycle-managed client connection backed by ClientWebSocket.

Raw WebSockets are the right transport when the wire format belongs to someone else — telephony media streams, realtime speech APIs, agent host sockets — or when a service needs a long-lived outbound channel with minimal overhead. The package supplies what the platform does not: a receive loop with multi-frame accumulation, a reconnect loop with capped jittered backoff, token refresh per attempt, observable state, and deterministic disposal.

Message routing is a derived-class concern. The default seam decodes Cirreum's { "method", "payload" } envelope; bridging a third-party protocol means overriding it and owning the discriminator.

Usage

Derive a connection type. The framework-supplied context is its first constructor parameter; anything else resolves from the container as usual:

public sealed class VoiceConnection(WebSocketRemoteConnectionContext context)
    : WebSocketRemoteConnection(context) {

    public Task SendAudioAsync(ReadOnlyMemory<byte> pcm, CancellationToken ct = default) =>
        this.SendBytesAsync(pcm, WebSocketMessageType.Binary, ct);

    // The provider owns this wire format, so this connection reads the frames itself
    // rather than through the Cirreum envelope.
    protected override ValueTask OnFrameReceivedAsync(
        ReadOnlyMemory<byte> payload, WebSocketMessageType messageType, CancellationToken ct) {

        // decode the provider's protocol
        return ValueTask.CompletedTask;
    }

}

For a Cirreum server on the other end, the default routing already matches what it sends — register handlers by method name and send through the envelope:

public sealed class NotificationConnection(WebSocketRemoteConnectionContext context)
    : WebSocketRemoteConnection(context) {

    public IDisposable OnNotice(Func<Notice, Task> handler) => this.On("Notice", handler);

    public Task AcknowledgeAsync(string id, CancellationToken ct = default) =>
        this.SendAsync("Acknowledge", id, ct);

}

Register it, and connect when the caller is ready:

services.AddSingleton(sp => new VoiceConnection(
    WebSocketRemoteConnectionContext.Create<VoiceConnection>(sp, new RemoteConnectionOptions("MyApp") {
        EndpointUri = new Uri("wss://provider.example.com/realtime"),
        Scopes = ["api://contoso/access_as_user"],
    })));

For a connection whose lifetime is a session rather than the application — one per phone call, one per bridge — construct one per session and dispose it with the session:

await using var voice = new VoiceConnection(
    WebSocketRemoteConnectionContext.Create<VoiceConnection>(services, options));

await voice.ConnectAsync(ct);

Applications composing through a Cirreum application builder normally register through the matching Runtime Extensions package instead, which reduces both shapes to a single builder call and owns the per-session lifetime rather than leaving each application to construct and track its own.

What the base owns

  • Receive loop — assembles multi-frame messages and hands each complete message to OnFrameReceivedAsync. A fault in that method is logged, not fatal to the connection.

  • Reconnect loop — raw WebSockets have none of their own. Retries indefinitely with capped, jittered backoff, driving the same state machine. Override OnReconnectedAsync to restore server-side state that does not survive a reconnect.

  • Credentials — resolved on every connect and reconnect attempt. A ClientWebSocket is single-use, so each attempt builds a fresh one and re-reads the credential; refresh is a consequence of that rather than extra machinery. Postures resolve in a fixed order, and the first one set wins:

    # Posture Set by
    1 CredentialProvider a callback on the connection's options
    2 AuthorizationHeader carrying a value the connection's options
    3 AuthorizationHeaderSettings.None the connection's options, to connect deliberately without one
    4 the ambient IRemoteConnectionCredentialSource the host runtime, or the application

    The ambient source is told what it is supplying for — endpoint, the Scopes the options declare, and the connection type — and a source registered keyed to that type is preferred over the unkeyed one, so one connection can use a different mechanism than another.

    A resolved credential has three answers: a value to present, None to connect without one, and null meaning none is available — which fails the connect rather than connecting anonymously.

    Any scheme may be resolved per attempt, not only Bearer, because the socket's headers are set after the credential resolves. Off-browser the credential travels as an Authorization header. In a browser, which cannot set headers on an upgrade, it travels as an access_token query parameter — and only Bearer has that equivalent, so a non-Bearer credential in a browser is rejected rather than silently dropped.

  • State and identity — State and StateChanged report the connection's lifecycle; ConnectionId is assigned by the adapter and stable across reconnects. SubProtocol reports what the server selected, re-read per connection because each reconnect negotiates afresh.

  • Sends are serialized — a WebSocket permits one write at a time, so concurrent callers queue rather than corrupting the stream.

There is no request/response: the transport provides none, and synthesizing one would mean rebuilding correlation, timeouts and cancellation over a one-way pipe.

Documentation

Contribution Guidelines

  1. Be conservative with new abstractions
    The API surface must remain stable and meaningful.

  2. Limit dependency expansion
    Only add foundational, version-stable dependencies.

  3. Favor additive, non-breaking changes
    Breaking changes ripple through the entire ecosystem.

  4. Include thorough unit tests
    All primitives and patterns should be independently testable.

  5. Document architectural decisions
    Context and reasoning should be clear for future maintainers.

  6. Follow .NET conventions
    Use established patterns from Microsoft.Extensions.* libraries.

Versioning

Cirreum.RemoteConnections.WebSockets follows Semantic Versioning:

  • Major - Breaking API changes
  • Minor - New features, backward compatible
  • Patch - Bug fixes, backward compatible

License

This project is licensed under the MIT License - see the LICENSE file for details.


Cirreum Foundation Framework
Layered simplicity for modern .NET

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Cirreum.RemoteConnections.WebSockets:

Package Downloads
Cirreum.Runtime.RemoteConnections.WebSockets

Runtime Extensions package for Cirreum raw WebSocket remote connections — app-facing AddRemoteConnection<TConnection>() and AddRemoteConnectionFactory<TConnection>() extensions that register a typed, lifecycle-managed WebSocket client connection from options.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.2 127 8/30/2026
2.0.0 133 8/25/2026
1.0.1 118 8/25/2026
1.0.0 108 8/24/2026