feat(web): per-relay listing control action + meshctrl list-relays (VS-31) #51

Merged
David merged 1 commit from feat/relay-listing-vs-31 into main 2026-06-07 01:45:29 +02:00
Owner

Implements VS-31: a per-relay listing control action so UIs (the vervain-apps Relays page) can render a table instead of the two aggregate serverstats counters.

Registry

RelayRegistry (meshcentral-relay) grows session-row tracking alongside the existing stash map:

  • track(info) -> SessionGuard: live-pump registration; the RAII guard retires the row when the pump ends.
  • stash_tracked(id, buffer, info): ties a row to a long-lived stash entry; take/drop retire it. Used where the stash entry IS the session (web relays). Transient rendezvous stashes stay untracked.
  • snapshot(): clones rows oldest-first; the lock is released before serialization.

Rows: id, kind, usage, nodeid, userid, peer_addr, registry-stamped established_ms. Byte counters deliberately absent: the registry never observes pumped bytes, and the spec says not to invent them.

Registration sites

  • meshrelay joining side: one agent-tunnel row per live tunnel (terminal/KVM/files, including VS-3 device-file tunnels).
  • /mcptunnel.ashx: mcp rows for the pump's lifetime.
  • open_web_relay: web-relay rows tied to the stash entry until /webrelay/<id> consumes it.

Control action + CLI

  • {"action":"relays"}, site-admin only, replies {"action":"relays","relays":[...]} across all three registries; unknown fields are omitted, not nulled.
  • Non-admin: explicit denial ({"relays":[],"result":"not-authorized"}, mirroring the users denial shape) rather than the serverstats silent-ignore, so a CLI waiting on the reply frame fails fast. Documented on the enum variant per the AC.
  • meshctrl list-relays: tabular output, bails with the server's denial message. The stale meshctrl doc-header intro now points at the Cmd enum as the source of truth (full header audit stays VS-30).

Acceptance criteria

  • One row per active session with the documented fields: covered by live_tunnel_rows_appear_in_listing_and_retire_on_close, which drives a real browser+agent rendezvous pair end-to-end, asserts the row on join, reads it back over /control.ashx, and asserts retirement on close.
  • Non-admin handling: explicit denial chosen and documented; covered by non_admin_gets_explicit_denial.
  • Rows appear/disappear as tunnels open and close: asserted in the same end-to-end test; please double-check against a live agent terminal session in staging before resolving.
  • meshctrl list-relays ships in this PR.

cargo test --workspace, cargo fmt --check, and cargo clippy --workspace --all-targets are clean. ROADMAP row removal is left to the post-merge roadmap sync, matching the VS-3 flow.

Resolves VS-31.

Implements VS-31: a per-relay listing control action so UIs (the vervain-apps Relays page) can render a table instead of the two aggregate `serverstats` counters. ## Registry `RelayRegistry` (meshcentral-relay) grows session-row tracking alongside the existing stash map: - `track(info) -> SessionGuard`: live-pump registration; the RAII guard retires the row when the pump ends. - `stash_tracked(id, buffer, info)`: ties a row to a long-lived stash entry; `take`/`drop` retire it. Used where the stash entry IS the session (web relays). Transient rendezvous stashes stay untracked. - `snapshot()`: clones rows oldest-first; the lock is released before serialization. Rows: `id`, `kind`, `usage`, `nodeid`, `userid`, `peer_addr`, registry-stamped `established_ms`. Byte counters deliberately absent: the registry never observes pumped bytes, and the spec says not to invent them. ## Registration sites - meshrelay joining side: one `agent-tunnel` row per live tunnel (terminal/KVM/files, including VS-3 device-file tunnels). - `/mcptunnel.ashx`: `mcp` rows for the pump's lifetime. - `open_web_relay`: `web-relay` rows tied to the stash entry until `/webrelay/<id>` consumes it. ## Control action + CLI - `{"action":"relays"}`, site-admin only, replies `{"action":"relays","relays":[...]}` across all three registries; unknown fields are omitted, not nulled. - Non-admin: explicit denial (`{"relays":[],"result":"not-authorized"}`, mirroring the `users` denial shape) rather than the serverstats silent-ignore, so a CLI waiting on the reply frame fails fast. Documented on the enum variant per the AC. - `meshctrl list-relays`: tabular output, bails with the server's denial message. The stale meshctrl doc-header intro now points at the `Cmd` enum as the source of truth (full header audit stays VS-30). ## Acceptance criteria - One row per active session with the documented fields: covered by `live_tunnel_rows_appear_in_listing_and_retire_on_close`, which drives a real browser+agent rendezvous pair end-to-end, asserts the row on join, reads it back over `/control.ashx`, and asserts retirement on close. - Non-admin handling: explicit denial chosen and documented; covered by `non_admin_gets_explicit_denial`. - Rows appear/disappear as tunnels open and close: asserted in the same end-to-end test; please double-check against a live agent terminal session in staging before resolving. - `meshctrl list-relays` ships in this PR. `cargo test --workspace`, `cargo fmt --check`, and `cargo clippy --workspace --all-targets` are clean. ROADMAP row removal is left to the post-merge roadmap sync, matching the VS-3 flow. Resolves VS-31.
feat(web): per-relay listing via relays control action + meshctrl verb
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 1m36s
Create release / Create release from merged PR (pull_request) Has been skipped
fd987121bf
RelayRegistry grows session-row tracking: track() registers a live row whose RAII guard retires it when the pump ends, stash_tracked() ties a row to a long-lived stash entry so take/drop retire it, and snapshot() clones the rows oldest-first without holding the lock across serialization. Rows carry id, kind, usage, nodeid, userid, peer address, and a registry-stamped established_ms; byte counters are deliberately absent because the registry never sees the pumped bytes.

Registration sites: the meshrelay joining side registers one "agent-tunnel" row per live tunnel (covers terminal/KVM/files tunnels including the VS-3 device-file path), /mcptunnel.ashx registers "mcp" rows for the pump's lifetime, and open_web_relay switches to stash_tracked so the stash entry that IS the web-relay session shows as a "web-relay" row until /webrelay/<id> consumes it.

New ControlIn::Relays action (site-admin only) replies {"action":"relays","relays":[...]} via relays_frame() across all three registries, omitting unknown fields rather than nulling them. Non-admins get an explicit denial ({"relays":[],"result":"not-authorized"}, mirroring the users denial shape) instead of the serverstats silent-ignore so CLI callers waiting on the reply frame fail fast; documented on the enum variant.

meshctrl gains list-relays (roundtrip on the relays action, tabular output, bails on the denial result) and its stale doc header intro now points at the Cmd enum as the source of truth instead of falsely claiming only list-meshes is wired.

Tests: relay-crate units for guard-drop retirement, stash-tracked retirement on take and on drop, and untracked stashes staying invisible; a user_session unit asserting the web-relay row round-trips through relays_frame; and an end-to-end test driving a real browser+agent rendezvous pair that asserts the row appears on join with the documented fields, is returned over /control.ashx to an admin, retires on close, and that a non-admin receives the explicit denial.

#VS-31
David merged commit 5e95113a7a into main 2026-06-07 01:45:29 +02:00
David deleted branch feat/relay-listing-vs-31 2026-06-07 01:45:29 +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-server!51
No description provided.