DarkWS 4.0.0

dotnet add package DarkWS --version 4.0.0
                    
NuGet\Install-Package DarkWS -Version 4.0.0
                    
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="DarkWS" Version="4.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DarkWS" Version="4.0.0" />
                    
Directory.Packages.props
<PackageReference Include="DarkWS" />
                    
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 DarkWS --version 4.0.0
                    
#r "nuget: DarkWS, 4.0.0"
                    
#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 DarkWS@4.0.0
                    
#: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=DarkWS&version=4.0.0
                    
Install as a Cake Addin
#tool nuget:?package=DarkWS&version=4.0.0
                    
Install as a Cake Tool

DarkWS

ASP.NET Core WebSocket request/response library with typed sessions and an in-memory broadcast backplane.

builder.Services
    .AddDarkWs()
    .AddHandlersFromAssemblyContaining<Program>()
    .AddAuthenticator<AppAuthenticator, AppSession>();

app.UseWebSockets();
app.MapDarkWs("/ws");
public sealed record AppSession(
    string Id,
    ClaimsPrincipal User,
    Guid AccountId
) : IDarkWsSession {
    public IReadOnlyCollection<string> Groups => [$"account:{AccountId}"];
}

[Handler("message")]
public sealed class MessageHandler : HandlerBase<AppSession> {
    [Action("send")]
    public async Task<IResponse> SendAsync(MessageInput input) {
        await BroadcastToGroupAsync($"account:{Session.AccountId}", "message:created", input);
        return Ok();
    }
}

Handlers require an authenticated session by default. Add [AllowAnonymous] to public handlers or actions. Register AddSession and call UseSession before MapDarkWs when handlers need ASP.NET ISession.

Incoming message limit

DarkWsOptions.MaxMessageSizeBytes limits a complete incoming message in bytes, including all fragments, before JSON parsing. The default is 1 MiB (1048576 bytes); values must be positive. Messages exactly at the limit are accepted. Exceeding the limit closes the connection with status 1009 (Message Too Big), without dispatching the partial message. This also applies to authentication messages.

services.AddDarkWs(options => options.MaxMessageSizeBytes = 256 * 1024);

Request concurrency and liveness

MaxConcurrentRequestsPerConnection defaults to 16 and must be positive. At capacity, request dispatch waits for a running request before accepting more work. Socket reads slow down; responses can arrive out of order and use id for correlation. The host or reverse proxy must enforce total connection and per-user/IP limits. Slow handlers can delay reading control messages at capacity.

KeepAliveInterval defaults to 30 seconds. On .NET 9/10, KeepAliveTimeout (30 seconds) enables transport PING/PONG failure detection. On .NET 8, ReceiveIdleTimeout (2 minutes) aborts a connection when a pending socket read times out. Each received fragment resets this timer; it is inactive while backpressure pauses reads. Idle clients must send application traffic such as text ping; the browser client does so every 30 seconds by default. Transport PONGs do not reset the application receive timer. All timeout options must be positive and at most 4294967294 milliseconds.

Registration contract

Call AddDarkWs() once and reuse its builder for additional handler assemblies. Repeated calls throw InvalidOperationException without replacing the registry. Only actions declared on the scanned handler class are registered; inherited actions must be declared or overridden there and marked with [Action]. Attributed methods must be public instance methods returning exactly IResponse or Task<IResponse> with zero or one payload parameter. Unsupported signatures (including generic methods and by-reference/byref-like/pointer payloads) throw InvalidOperationException naming the type, method, and reason during registration.

Options, payloads, and context

The standard Configure, configuration binding, and PostConfigure pipeline is supported. Final options are validated on resolution and host startup with OptionsValidationException. Existing connections and singleton services retain their captured settings. Non-nullable parameters require a non-null payload; nullable parameters permit omitted/null values. Invalid payloads return darkws:error:invalid-request before constructing or invoking the handler.

An injected IDarkWsContextAccessor is initialized only inside a message scope. Other scopes receive InvalidOperationException on property access. Lifecycle middleware should use the context supplied to its hook.

Shutdown and authentication

SendTimeout defaults to 30 seconds and includes send-lock wait and transport write. Expiration aborts the socket. ShutdownTimeout bounds the whole shutdown: pending tasks, close hooks, and handshake. Connections leave storage immediately; handler tokens are cancelled. Handlers must cooperate with cancellation. Tasks that ignore it retain their scope/connection resources until actual completion, while the socket is aborted and further responses are suppressed.

System commands are plain text: auth:<token> receives auth:success or auth:failed; logout receives logout:success; ping receives pong. Rejected authentication and logout clear the previous session. OnAuthenticatedAsync runs after success, rejection, and logout, and its current session can be null. JSON authentication/logout actions and @auth replies are no longer used. The legacy AuthenticationFailedError option does not customize text replies. Token expiry and revocation enforcement remain the application's responsibility; already running actions are not rolled back.

Session and group indexes refresh on registration and re-authentication. Re-add the connection with ConnectionStorage.Add after external group changes. XML API documentation and .snupkg symbols with embedded sources are included.

Compatibility and diagnostics

See the repository CHANGELOG before upgrading from 2.0.0: 2.1.0 introduced the 1 MiB default limit and closes oversized input with status 1009. Configure MaxMessageSizeBytes explicitly if your application legitimately needs more.

Request ids @ and @auth are reserved. Invalid requests using them receive invalid-request with an empty id, so errors cannot be mistaken for broadcasts. Empty response error codes are rejected at construction. Domain exceptions retain their code in Message and support an optional inner exception. Public object boundaries report null arguments with ArgumentNullException.

Successful actions and malformed requests use Debug logs; unexpected failures remain warnings. Static callers should use DarkWsServiceCollectionExtensions and DarkWsEndpointRouteBuilderExtensions. The old Configuration wrapper is obsolete; extension-call syntax remains unchanged.

Native AOT and trimmed publishing are unsupported because handler discovery, delegate compilation, and JSON serialization depend on reflection/runtime code generation. Use ordinary JIT publishing. The .NET 8/9/10 targets are intentional: their liveness mechanisms differ.

Only [AllowAnonymous] is supported for action authorization. [Authorize] and other IAuthorizeData on handlers/actions fail registration; implement domain role/policy checks inside handlers and return controlled errors on denial.

Query-string tokens can be recorded by proxies and access logs. Use short-lived tickets or, where the host/authenticator permits an anonymous upgrade, authenticate after connecting instead of putting credentials in the URL. Use WSS, redact credential logging, and enforce token lifetime/revocation in the application.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on DarkWS:

Package Downloads
DarkWS.Redis

Redis backplane for DarkWS

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.0.0 97 9/15/2026
3.0.0 94 9/14/2026
2.1.0 91 9/13/2026
2.0.0 124 9/5/2026