Fuaran.UI.ServerDriven.WebSocket 0.46.0

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

Fuaran.UI.ServerDriven.WebSocket

Backend 2 for Fuaran.UI.ServerDriven — one bidirectional WebSocket channel over ASP.NET. The lower-latency transport for measured high-frequency interaction (per-keystroke, drag, pointer-move, live cursors).

This is a first-class second backend, not a placeholder. Its value is twofold: the latency win above, and — structurally — it differs from the SSE+POST backend only in its channel + endpoint glue (WsChannel mirrors SseChannel; the driver / diff / lowering core is untouched). That structural identity is the architectural-integrity check that IFuaranLiveChannel is genuinely transport-neutral and not accidentally SSE-shaped.

That identity is now asserted rather than asserted-about. The two backends share one connection-token implementation and one inbound budget, both in the transport-agnostic core, and a transport-parity test pins their configs against each other — because for a while this README's neighbouring config comment claimed a security parity the code did not have, and a claim no test reads is just a sentence. Neither package references the other; what they share, they share through the core.

Wiring

open Fuaran.UI.ServerDriven.Driver
open Fuaran.UI.ServerDriven.WebSocket.Endpoints

app.UseWebSockets() |> ignore   // required

let makeSession () =
    Driver.init (DriverServices.create renderFragment) update view initialModel

mapFuaranLiveWebSocket app (defaultWsConfig makeSession)

Connecting: mint, then upgrade

mapFuaranLiveWebSocket maps two endpoints. GET /live/ws-token resolves the request principal, mints a connection token bound to it, and sets it as a hardened correlation cookie (HttpOnly + Secure + SameSite=Strict). GET /live/ws then upgrades only if that cookie verifies against the principal on the upgrade request; anything else is a 401 and no socket is accepted. So a client fetches the token first, with credentials, and then connects:

await fetch("/live/ws-token", { credentials: "same-origin" })
const ws = new WebSocket(`wss://${location.host}/live/ws`)

The order matters and the split is not ceremony. A WebSocket upgrade is a single request that both opens and authorises the session, so there is no second request to gate the way the SSE backend gates its POST — the token has to exist before the handshake or there is nothing to check. Two endpoints give the two transports the same shape: one mints the principal-bound token, the other refuses anything not carrying it.

SameSite=Strict is doing specific work here. A browser applies no same-origin policy to a WebSocket handshake, so an attacker's page can open a socket to your server with the victim's cookies attached — cross-site WebSocket hijacking. A Strict cookie is not sent on that handshake, so the upgrade carries no token and is refused.

The host must still layer authentication in front of these paths. The principal resolver reflects whatever identity that authentication established; with none wired, every visitor resolves to the empty principal and the token closes forgeability but not authorisation.

Inbound messages are capped (1 MB by default, MaxMessageBytes), because the fragment accumulator only flushes at EndOfMessage and an endless fragment stream would otherwise grow server memory without bound. Exceeding the budget closes the socket with MessageTooBig (1009). The budget itself lives in Fuaran.UI.ServerDriven.LiveLimits, shared with the SSE backend.

GET /live/ws upgrades to a WebSocket; the same socket carries outbound frames (FrameWire.encodeJson — raw JSON, no SSE id:/event: framing) and inbound events (parsed → LiveConnection.Handle → G1 → driver → frames back). The client uses the shim's WebSocket transport adapter (a Track-B drop-in — only the connect/send adapter changes; the patch/effect/delegation core is identical).

SSE+POST vs WebSocket

Default to SSE+POST (Fuaran.UI.ServerDriven.AspNetCore): it traverses infra natively, gets per-event HTTP governance for free, and reconnects via Last-Event-ID. Reach for WebSocket when a measured interaction pattern needs the lower per-message latency. The IFuaranLiveChannel seam makes that a swap, not a rewrite. WS owns its own reconnect + resequencing client-side (no EventSource freebie); the server half is Frame.Seq + LiveConnection.Resync. See fuaran-dotnet/docs/SERVER_DRIVEN.md.

No platform-SDK dependency. Apache-2.0 licensed — see the repo LICENSE.

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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.46.0 0 8/28/2026
0.39.0 54 8/27/2026
0.35.0 83 8/24/2026
0.32.0 79 8/23/2026
0.31.0 89 8/21/2026
0.30.0 88 8/21/2026
0.29.0 91 8/19/2026
0.28.0 83 8/18/2026
0.27.0 86 8/18/2026
0.26.0 98 8/18/2026
0.18.0 94 8/10/2026
0.15.0 88 8/9/2026
0.12.0 102 7/31/2026
0.11.0 99 7/30/2026
0.10.0 101 7/29/2026
0.6.0 100 7/27/2026
0.4.0 101 7/26/2026
0.3.0 112 7/24/2026