Repository navigation
RFW 2.3.0 makes authenticated, multi-user applications safer to operate and brings the first production-oriented Capacitor path to iOS. It also prevents slow SSC clients from stalling broadcasts and adds opt-in render tracing for measuring real browser workloads.
Multi-user session and route safety
Authenticated applications can now bind SSC resume to the identity and revocation state of the reconnecting request with host.WithSSCResumeAuthorizer. A rejected resume leaves the retained session untouched and gives the caller a fresh session, closing the cross-user token-reuse gap while preserving the existing token-only behavior for applications that do not opt in.
Host component subscriptions now follow mounted component ownership. AddHostComponent registrations are released on unmount, late frames cannot write into a stale DOM root or host signal, and offline mount/unmount churn is coalesced instead of growing the reconnect queue indefinitely.
Routing gains declarative authorization outcomes through router.ResultGuard:
Allow()andForbid()for ordinary decisions;RedirectTo()andReplaceWith()for SPA-owned destinations;HostReplace()for rooted same-origin pages owned by the host, such as login or password-reset flows;Revalidate()/RevalidateContext()to apply changed claims to the mounted route without replacing the persistent shell.
Denied revalidation tears down the routed owner, its scope, host registrations and routed DOM exactly once. Allowed revalidation is a no-op. Loaders and factories do not run for denied routes.
Resilient SSC delivery
Each configured WebSocket connection now owns a bounded outbound queue and a dedicated writer with a write deadline. Broadcast performs non-blocking enqueue work, so a client behind a stalled TCP connection cannot delay healthy subscribers later in the same broadcast.
Queue overflow never silently drops a sequenced frame. The affected connection is closed with a best-effort resync_required control message, and the existing retained-session replay path restores the ordered stream after resume. The conservative defaults are a 10-second write timeout and 64 queued outbound batches, configurable through host.WithSSCLimits.
Capacitor and embedded WASM delivery
rfw.json now supports build.delivery: "embedded" for optimized raw WASM packaged inside a native container. It is independent from build.type: an embedded SSC application still uses its remote host, but the local asset handler no longer needs to emulate HTTP content negotiation for app.wasm.
The new opt-in build.sscTransport: "capacitor" selects the included iOS Capacitor SSC plugin. It uses URLSessionWebSocketTask and the native shared cookie jar, enforces an exact WSS-origin allowlist, bounds inbound and outbound traffic, rejects unsafe redirects and configuration, and closes while the app is backgrounded. Transport selection fails closed when Capacitor or the plugin is unavailable; it never silently falls back to a browser WebSocket.
hostclient.ResetSession() completes the identity-change contract. It closes the active SSC transport, fences late frames, clears resume and sequencing state, rejects interrupted calls with hostclient.ErrSessionReset, and reconnects surviving app-lifetime registrations under the new authenticated cookie identity.
The Capacitor transport in this release is iOS only. Android runtime and physical-device support are not part of 2.3.0.
Render observability
Applications can enable versioned rfw:render-trace browser events before WASM startup. The trace distinguishes scheduler requests, coalescing, RTML evaluation, DOM commit, outlet work, cancellation, failure and unmount cleanup. It is disabled by default, retains no framework-side event buffer and adds no production dependency.
Compatibility and migration
There are no required source migrations for existing applications:
- network WASM delivery and browser SSC remain the defaults;
- SSC resume remains token-only unless a resume authorizer is configured;
- legacy bool guards and the existing route builders remain available;
core.SetHostRegisterandhostclient.RegisterComponentretain their signatures.
Authenticated multi-user deployments should configure WithSSCResumeAuthorizer or disable resume. When changing identity, first unmount user-owned scopes and clear user-owned state, then call hostclient.ResetSession() before installing the replacement session. A project that overrides wasm_loader.js must update or remove that override before opting into embedded delivery.
Release verification
The release candidate is ca94b7c59aa50013f6a2b090e0186c012b9969b7. The release workflow validates native tests and build, the complete Chrome/WebAssembly suite, native and WASM lint, iOS native SSC tests, secret scanning and EUProvGuard before publishing platform binaries, checksums and compliance reports.
Related work: render tracing #70, bounded SSC delivery #71, multi-user lifecycle boundaries #72, and Capacitor mobile runtime #74.
Full Changelog: v2.2.1...v2.3.0