VAPP-14: Live updates: persistent control connection with server push #20

Merged
David merged 2 commits from feat/live-control-connection-vapp-14 into main 2026-06-07 15:10:15 +02:00
Owner

Summary

Implements VAPP-14: live updates over a persistent control connection. The browser now holds ONE long-lived /control.ashx connection open and patches the UI from server-pushed frames, so device status, the dashboard devices-online KPI, the events list, and the statusbar all update without a reload.

Unblocked by VS-32 (token-authenticated /control.ashx upgrade), now merged into vervain-server main.

How it works

  • get_control_token (server fn) mints the single-use, ~60 s upgrade token via the authcookie control action's new controlToken field (VS-32) and returns the browser-facing wss://host/control.ashx?auth=<token> URL. The DIRECT path is required because the apps origin holds no mc_session cookie.
  • src/live.rs is a root-level service (provided in main.rs) that opens the socket with reconnect-and-backoff and exposes three signals over context: connection state, a node-id -> online connectivity map (from pushed nodeconnect frames), and a capped newest-first event feed. A fresh token is minted per (re)connect since the token is single-use.
  • Pages subscribe: devices list + device-detail overlay layer live conn over fetched rows; the dashboard recomputes devices-online from the device list plus the connectivity map; the events page prepends pushed entries (deduped by id); the statusbar binds to the connection-state signal (live / reconnecting / disconnected).
  • Initial page data still loads through the existing server fns, so SSR keeps working; the live layer only patches deltas on top.
  • Frame decoding (decode_frame) is a pure function shared by the wasm driver and tests, mirroring the server-side event mapping so a pushed event renders identically to a fetched one.

Acceptance criteria

  • Stopping an agent flips its row to Offline on /devices within seconds, no reload (connectivity map layered over fetched rows)
  • Statusbar shows live / reconnecting / disconnected truthfully (bound to the connection-state signal; backoff escalates to disconnected when the server stays unreachable)
  • New events appear at the top of /events while the page is open (pushed event-log frames prepended, deduped by id)
  • Reconnect after server restart resumes pushes without manual reload (reconnect-with-backoff loop mints a fresh token and reopens)
  • Linked as depends-on to the VS token-upgrade issue (VS-32, already linked)

Testing

just check equivalents all green locally:

  • cargo fmt --check
  • cargo clippy --all-targets --features server -- -D warnings (and --target wasm32-unknown-unknown)
  • cargo check --features server and cargo check --target wasm32-unknown-unknown
  • cargo test --features server --bin vervain-app (15 passed, 7 new covering decode_frame: nodeconnect online/offline/missing-conn/missing-nodeid, logged-event mapping, source fallback, ignore of connect-burst/ack/malformed frames)

🤖 Generated with Claude Code

## Summary Implements VAPP-14: live updates over a persistent control connection. The browser now holds ONE long-lived `/control.ashx` connection open and patches the UI from server-pushed frames, so device status, the dashboard devices-online KPI, the events list, and the statusbar all update without a reload. Unblocked by VS-32 (token-authenticated `/control.ashx` upgrade), now merged into vervain-server `main`. ## How it works - `get_control_token` (server fn) mints the single-use, ~60 s upgrade token via the `authcookie` control action's new `controlToken` field (VS-32) and returns the browser-facing `wss://host/control.ashx?auth=<token>` URL. The DIRECT path is required because the apps origin holds no `mc_session` cookie. - `src/live.rs` is a root-level service (provided in `main.rs`) that opens the socket with reconnect-and-backoff and exposes three signals over context: connection state, a node-id -> online connectivity map (from pushed `nodeconnect` frames), and a capped newest-first event feed. A fresh token is minted per (re)connect since the token is single-use. - Pages subscribe: devices list + device-detail overlay layer live `conn` over fetched rows; the dashboard recomputes devices-online from the device list plus the connectivity map; the events page prepends pushed entries (deduped by id); the statusbar binds to the connection-state signal (live / reconnecting / disconnected). - Initial page data still loads through the existing server fns, so SSR keeps working; the live layer only patches deltas on top. - Frame decoding (`decode_frame`) is a pure function shared by the wasm driver and tests, mirroring the server-side event mapping so a pushed event renders identically to a fetched one. ## Acceptance criteria - [x] Stopping an agent flips its row to Offline on /devices within seconds, no reload (connectivity map layered over fetched rows) - [x] Statusbar shows live / reconnecting / disconnected truthfully (bound to the connection-state signal; backoff escalates to `disconnected` when the server stays unreachable) - [x] New events appear at the top of /events while the page is open (pushed event-log frames prepended, deduped by id) - [x] Reconnect after server restart resumes pushes without manual reload (reconnect-with-backoff loop mints a fresh token and reopens) - [x] Linked as depends-on to the VS token-upgrade issue (VS-32, already linked) ## Testing `just check` equivalents all green locally: - `cargo fmt --check` - `cargo clippy --all-targets --features server -- -D warnings` (and `--target wasm32-unknown-unknown`) - `cargo check --features server` and `cargo check --target wasm32-unknown-unknown` - `cargo test --features server --bin vervain-app` (15 passed, 7 new covering `decode_frame`: nodeconnect online/offline/missing-conn/missing-nodeid, logged-event mapping, source fallback, ignore of connect-burst/ack/malformed frames) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(live): persistent control connection with server push
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 1m54s
05162781bc
Add the legacy SPA's missing half: one long-lived `/control.ashx` connection the wasm client holds open, receiving server-pushed `nodeconnect` and event-log frames, so device status, the dashboard devices-online KPI, the events list, and the statusbar update without a reload.

The connection is DIRECT (parity-review decision): `get_control_token` mints the single-use upgrade token via the `authcookie` action's `controlToken` field (VS-32) and returns the browser-facing `wss://host/control.ashx?auth=<token>` URL the client dials itself, since the apps origin holds no `mc_session` cookie.

A root-level live service (`src/live.rs`, provided in `main.rs`) opens the socket with reconnect-and-backoff and exposes three signals over context: connection state, a node-id -> online connectivity map (from pushed `nodeconnect` frames), and a capped newest-first event feed. Pages subscribe: the devices list and device-detail overlay layer live `conn` over fetched rows, the dashboard recomputes devices-online from the device list plus the connectivity map, the events page prepends pushed entries (deduped by id), and the statusbar binds to the connection-state signal (live / reconnecting / disconnected). Initial page data still loads through the existing server fns (SSR keeps working); the live layer only patches deltas.

Frame decoding is a pure, unit-tested function (`decode_frame`) shared by the wasm driver and tests; it mirrors the server-side event mapping so a pushed event renders identically to a fetched one.

#VAPP-14

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merge branch 'main' into feat/live-control-connection-vapp-14
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt + clippy + build + tests (pull_request) Successful in 1m29s
dc39a1ca05
David merged commit 0b7ea818ef into main 2026-06-07 15:10:15 +02:00
David deleted branch feat/live-control-connection-vapp-14 2026-06-07 15:10:15 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/vervain-apps!20
No description provided.