From 3fcacfbe6f236a602a8702dba35a3650d6328d3a Mon Sep 17 00:00:00 2001 From: damocles Date: Sun, 31 May 2026 20:03:38 +0200 Subject: [PATCH] docs(715): scrub 30 issue cookies from dashboard.rs --- hive-c0re/src/dashboard.rs | 84 +++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/hive-c0re/src/dashboard.rs b/hive-c0re/src/dashboard.rs index f73c1e2a..617418a1 100644 --- a/hive-c0re/src/dashboard.rs +++ b/hive-c0re/src/dashboard.rs @@ -90,7 +90,7 @@ pub async fn serve(port: u16, coord: Arc) -> Result<()> { // /static/dashboard.css → dist/static/dashboard.css, etc.). .fallback_service(ServeDir::new(&static_dir)) .with_state(AppState { coord }); - // Bind loopback-only (#652). External access funnels through + // Bind loopback-only. External access funnels through // hive-gateway (in-host-netns nginx container), which proxies // `/` → `127.0.0.1:` upstream. Operators who opt // out of the gateway lose remote dashboard access — that's by @@ -118,8 +118,8 @@ pub async fn serve(port: u16, coord: Arc) -> Result<()> { /// `SO_REUSEADDR` bind with retry. Mirrors the per-agent variant in /// `hive-ag3nt::web_ui::bind_with_retry`: hive-c0re restarts also /// race the previous process's socket release, and the retry has no -/// attempt cap — capping was the proximate cause of issue #324 -/// (silent give-up on a long stale socket). Genuine port collisions +/// attempt cap — capping was the proximate cause of a silent +/// give-up on a long stale socket. Genuine port collisions /// don't reach this layer (dashboard is bound to a fixed configured /// port, no per-agent hashing), so any persistent `AddrInUse` always /// reflects a recoverable stale socket. WARN for the first dozen @@ -216,7 +216,7 @@ struct StateSnapshot { /// agent rebuild ripple) is running in the background. Lets a /// client that cold-loads mid-update render the META INPUTS panel's /// disabled "updating…" state; live transitions arrive via the - /// `MetaUpdateRunning` event (issue #259). + /// `MetaUpdateRunning` event. meta_update_running: bool, /// Current state of the global rebuild queue — pending + running /// long-lived ops (rebuild / meta-update / spawn) plus the most @@ -233,8 +233,7 @@ struct StateSnapshot { /// when `services.hyperhive.matrix.gui.enable` is on). The gateway /// (hive-gateway.nix) does the actual `/matrix/` static serving; /// this flag is just an availability signal for iris's dashboard - /// chrome so the `M4TR1X →` tab doesn't flash when the GUI is off - /// (#607, #634). + /// chrome so the `M4TR1X →` tab doesn't flash when the GUI is off. matrix_gui_enabled: bool, /// Whether `hive-gateway` is in front of this dashboard. Sourced /// from `HIVE_GATEWAY_ENABLED` env var (set by the c0re NixOS @@ -339,7 +338,7 @@ struct ApprovalView { #[serde(skip_serializing_if = "Option::is_none")] description: Option, /// Unix seconds the approval was queued. Rendered as a relative - /// time on the card so the operator can spot a stale request. (#272) + /// time on the card so the operator can spot a stale request. requested_at: i64, } @@ -501,7 +500,7 @@ pub struct MetaInputView { /// slash-separated paths from root, the syntax `nix flake update` /// accepts for transitive inputs. /// -/// Filtering (see issue #275): +/// Filtering: /// - Inputs that resolve via a `follows` chain (lock value is an /// array) are skipped — they alias another node, not their own /// fetched derivation, so updating them does nothing. @@ -603,12 +602,12 @@ fn walk_meta_inputs( to_recurse.push((target_name.clone(), path)); } // Recurse hyperhive's subtree before any agent's — without this, - // when meta's top-level `nixpkgs` is a `follows` alias (post-#632 - // / #526) the `String` check above skips it, and the alphabetical - // BTreeMap iteration descends into `agent-*` first. The agent - // walk then claims `nixpkgs` at `agent-X/nixpkgs` instead of - // `hyperhive/nixpkgs`, which is where the operator expects it - // (closes #638). Sort by the same "hyperhive first, then alpha" + // when meta's top-level `nixpkgs` is a `follows` alias the + // `String` check above skips it, and the alphabetical BTreeMap + // iteration descends into `agent-*` first. The agent walk then + // claims `nixpkgs` at `agent-X/nixpkgs` instead of + // `hyperhive/nixpkgs`, which is where the operator expects it. + // Sort by the same "hyperhive first, then alpha" // priority `read_meta_inputs` uses for the final output. to_recurse.sort_by(|(a, _), (b, _)| match (a.as_str(), b.as_str()) { ("hyperhive", _) => std::cmp::Ordering::Less, @@ -865,8 +864,8 @@ async fn dashboard_history(State(state): State) -> Response { } } -/// `/dashboard/stream` query string. Today's only field is `kinds` -/// (#408): a comma-separated allow-list of event-`kind` strings. +/// `/dashboard/stream` query string. Today's only field is `kinds`: +/// a comma-separated allow-list of event-`kind` strings. /// Empty / absent ⇒ no filter (current behaviour, all variants /// forwarded). Set ⇒ only the named kinds reach the subscriber, /// non-matches are skipped before the JSON serialise cost. @@ -1085,10 +1084,10 @@ async fn get_journal( AxumPath(name): AxumPath, axum::extract::Query(q): axum::extract::Query, ) -> Response { - // Defense-in-depth format check (#572) so weird chars never reach - // the shellout below — the `lifecycle::list()` existence check - // would catch them anyway, but rejecting at the boundary keeps - // the failure mode crisp. + // Defense-in-depth format check so weird chars never reach the + // shellout below — the `lifecycle::list()` existence check would + // catch them anyway, but rejecting at the boundary keeps the + // failure mode crisp. if let Some(reason) = validate_agent_name(&name) { return (StatusCode::BAD_REQUEST, format!("bad agent name: {reason}")).into_response(); } @@ -1323,7 +1322,7 @@ mod tests { #[test] fn walk_meta_inputs_keeps_nixpkgs_under_hyperhive_post_follows_refactor() { - // Reproduce the post-#632 (and pre-fix #638) shape: meta has + // Reproduce the shape where meta has // `nixpkgs.follows = "hyperhive/nixpkgs"` at the top level // (rendered as an array — `["hyperhive" "nixpkgs"]` — which // walk_meta_inputs skips because we can't `nix flake update` @@ -1373,7 +1372,7 @@ mod tests { .expect("nixpkgs node should be emitted exactly once"); assert_eq!( nixpkgs.name, "hyperhive/nixpkgs", - "nixpkgs should be claimed under hyperhive, not under agent-z (closes #638). \ + "nixpkgs should be claimed under hyperhive, not under agent-z. \ got: {:?}", nixpkgs.name ); @@ -1405,8 +1404,8 @@ mod tests { // build that scaffolding for an integration-flavoured test we cover // the format axis here (the existence axis is enforced by the // shared `containers_snapshot` API, tested in `coordinator.rs`'s - // own suite). 9 cases below match what shipped in #581 + the new - // boundary-length case to make the contract explicit. + // own suite). 9 cases below cover the boundary-length case and + // other expected rejects to make the contract explicit. #[test] fn validate_agent_name_rejects_bad_input() { assert!(validate_agent_name("").is_some()); @@ -1629,7 +1628,7 @@ async fn api_reminders(State(state): State) -> Response { } /// `GET /api/schedules` — snapshot of every schedule for the -/// scheduled-prompts tab (#444). Returns the wire shape directly +/// scheduled-prompts tab. Returns the wire shape directly /// so the frontend can render without an extra translation layer. async fn api_schedules(State(state): State) -> Response { match state.coord.scheduled_prompts.list() { @@ -1678,7 +1677,7 @@ async fn post_schedule_new( } /// `POST /api/schedules/{id}/fire-now` — operator-initiated -/// out-of-band fire of a scheduled prompt (#467). Runs the +/// out-of-band fire of a scheduled prompt. Runs the /// per-target fan-out once immediately and reports per-target /// outcome counts. Does NOT touch `next_fire_at_unix` on /// recurring schedules (their cadence stays intact); one-shot @@ -1696,7 +1695,7 @@ async fn post_schedule_fire_now( } /// `POST /api/rebuild-queue/{id}/cancel` — drop a `Queued` entry -/// from the rebuild queue (#447). Refuses `Running` / terminal +/// from the rebuild queue. Refuses `Running` / terminal /// entries: an in-flight rebuild owns the agent's nix store + /// nixos-container update lock and can't be safely interrupted /// from the queue side. Always returns 200; the body is @@ -1772,9 +1771,9 @@ where } /// `PATCH /api/schedules/{id}` — partial update of an existing -/// schedule (#474). Mutable fields: `body`, `description`, +/// schedule. Mutable fields: `body`, `description`, /// `interval_seconds`, `next_fire_at_unix`, plus the target set -/// via `targets_add` / `targets_remove` (#478). Both target lists +/// via `targets_add` / `targets_remove`. Both target lists /// are applied in the same transaction as the scalar fields with /// removes-before-adds; re-adding a previously-removed target /// resets per-target history (fresh start); draining all targets @@ -1832,7 +1831,7 @@ async fn post_schedule_cancel( /// Same-origin proxy that fetches the named agent's /// `GET /api/state` and forwards only the `links` field to the -/// dashboard JS (issue #262). Lets the agent backend stay the +/// dashboard JS. Lets the agent backend stay the /// single source of truth for its own nav links: the dashboard /// card's icon-only strip and the per-agent page's labelled row /// render the same list, no shared Rust wire type required, no @@ -1841,9 +1840,9 @@ async fn post_schedule_cancel( /// Failure modes (agent down, slow response, malformed JSON) all /// degrade to an empty list so the dashboard still renders. async fn get_agent_links(AxumPath(name): AxumPath) -> Response { - // Format-only guard (#572). GET routes return empty gracefully - // on unknown names per argus's spec; bad format we reject early - // so the downstream port-hash + HTTP fetch never sees garbage. + // Format-only guard. GET routes return empty gracefully on + // unknown names; bad format we reject early so the downstream + // port-hash + HTTP fetch never sees garbage. if validate_agent_name(&name).is_some() { return axum::Json(serde_json::json!([])).into_response(); } @@ -1923,8 +1922,7 @@ async fn post_retry_reminder( /// homoglyphs of dash/underscore). Returns `None` on accept, `Some(reason)` /// on reject — caller wraps the reason in a 400 response. Conservative /// whitelist matching `nixos-container` basename rules and the existing -/// agent-name convention across the codebase. (mara nag on #566 → -/// shipped in #581; rolled out across every write route in #572.) +/// agent-name convention across the codebase. fn validate_agent_name(name: &str) -> Option<&'static str> { if name.is_empty() { return Some("agent name must not be empty"); @@ -1941,7 +1939,7 @@ fn validate_agent_name(name: &str) -> Option<&'static str> { None } -/// Two-axis path-param guard for write routes (#572). Combines: +/// Two-axis path-param guard for write routes. Combines: /// /// 1. **format validation** (`validate_agent_name`) — rejects path /// traversal / unicode homoglyphs / empty + too-long names with @@ -1973,7 +1971,7 @@ async fn guard_agent_name(state: &AppState, name: &str) -> Option { } /// Operator-driven "clear this agent's inbox" — backs the side-panel -/// "mark all read" button (#559). Marks every message addressed to the +/// "mark all read" button. Marks every message addressed to the /// agent as acked (backfilling `delivered_at` for any still-pending /// rows so vacuum can collect them). Returns `{ "marked": N }` so the /// frontend can show "cleared N messages" feedback without an extra @@ -2002,7 +2000,7 @@ async fn post_purge_tombstone( // parent of `/var/lib/hyperhive/agents/{name}` and have // `remove_dir_all` wipe `/var/lib/hyperhive/` itself. Existing // manager + live-container checks below don't catch `..` — only - // the whitelist does. (argus #593 🔴.) Existence check via + // the whitelist does. Existence check via // `containers_snapshot()` is deliberately NOT used here: // tombstoned agents are gone from the snapshot by design; that's // the whole point of this endpoint. @@ -2102,7 +2100,7 @@ async fn post_meta_update( inputs.clone(), ); // Pre-enqueue cascade rebuilds NOW so they're visible in the queue - // alongside the parent (issue #347). The worker's MetaUpdate arm + // alongside the parent. The worker's MetaUpdate arm // no longer enqueues children — it just runs the lock bump and // (on failure) cancels these pre-queued children. let cascade_agents = crate::rebuild_queue::meta_update_cascade_agents(&inputs).await; @@ -2183,8 +2181,8 @@ async fn post_request_spawn( } } -/// `POST /api/topology/set-parent` — operator-driven parent move -/// (#486). Form fields: `child` (required, agent name), `new_parent` +/// `POST /api/topology/set-parent` — operator-driven parent move. +/// Form fields: `child` (required, agent name), `new_parent` /// (optional — empty / absent string ⇒ promote to root). Refuses /// cycles, unknown agents, and reparenting the manager. On success /// re-emits container snapshots so the dashboard tree repaints @@ -2207,7 +2205,7 @@ async fn post_set_parent( .filter(|s| !s.is_empty()) .map(str::to_owned); // `reparent_with_notify` wraps `topology::set_parent` with the - // three notification messages (#743) + the ContainerView rescan. + // three notification messages + the ContainerView rescan. // Idempotent same-parent calls skip both the messages and the // disk write per the topology fast-path. match state @@ -2286,7 +2284,7 @@ async fn post_kill(State(state): State, AxumPath(name): AxumPath