hive-c0re, docs: sweep remaining stale ask/answer-dashboard references
This commit is contained in:
parent
47cac50e6e
commit
46183795dd
6 changed files with 47 additions and 50 deletions
|
|
@ -635,10 +635,13 @@ root agent. Variants (`hive_sh4re::manager::HelperEvent`):
|
||||||
- `NeedsUpdate { agent }` — sub-agent's recorded flake rev is
|
- `NeedsUpdate { agent }` — sub-agent's recorded flake rev is
|
||||||
stale. The root agent calls `update(name)` to rebuild — idempotent,
|
stale. The root agent calls `update(name)` to rebuild — idempotent,
|
||||||
no approval required.
|
no approval required.
|
||||||
- `QuestionAnswered { id, question, answer, answerer }` —
|
- `QuestionAnswered { id, question, answer, answerer }` — peer
|
||||||
dashboard `/answer-question/{id}` (answerer = `"operator"`),
|
`Answer` request (answerer = agent name) or ttl watchdog expiry
|
||||||
peer `Answer` request (answerer = agent name), or ttl watchdog
|
(answerer = `"ttl-watchdog"`, answer = `"[expired]"`). The dashboard's
|
||||||
expiry (answerer = `"ttl-watchdog"`, answer = `"[expired]"`).
|
own `/answer-question/{id}` trigger (answerer = `"operator"`) is
|
||||||
|
gone — removed along with the rest of the dashboard's ask/answer
|
||||||
|
surface, so an operator-targeted question now has no path to an
|
||||||
|
answer at all.
|
||||||
- `QuestionAsked { id, asker, question, options, multi }` —
|
- `QuestionAsked { id, asker, question, options, multi }` —
|
||||||
fired when an agent calls `Ask { to: Some(<this-agent>), ... }`.
|
fired when an agent calls `Ask { to: Some(<this-agent>), ... }`.
|
||||||
The recipient responds via `Answer { id, answer }` and the
|
The recipient responds via `Answer { id, answer }` and the
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,12 @@ always on), the gateway proxies all operator-facing traffic, and
|
||||||
**Operator-authority actions never get a per-agent-socket entry
|
**Operator-authority actions never get a per-agent-socket entry
|
||||||
point.** They live on the core backend.
|
point.** They live on the core backend.
|
||||||
|
|
||||||
Worked example — answering an operator-targeted question is a
|
Worked example — destroying or rebuilding a container is a
|
||||||
`POST /api/answer-question/{id}` on the core dashboard, _never_ a
|
`POST /api/{destroy,rebuild}/{name}` on the core dashboard, _never_ a
|
||||||
per-agent-socket `Request` variant. If it were a per-agent-socket
|
per-agent-socket `Request` variant. If it were a per-agent-socket
|
||||||
request, an agent could `curl` its own socket and spoof an operator
|
request, a compromised agent could `curl` its own socket and destroy
|
||||||
answer.
|
or rebuild itself (or, if the variant took an arbitrary target, another
|
||||||
The per-agent web UI POSTs cross-origin to the core for these
|
agent) without ever touching the core's own authenticated surface.
|
||||||
(see the inline-answer feature — the loose-ends section on each
|
|
||||||
agent page).
|
|
||||||
|
|
||||||
## Why network isolation is the load-bearing step
|
## Why network isolation is the load-bearing step
|
||||||
|
|
||||||
|
|
@ -67,9 +65,10 @@ The boundary rests on three layers:
|
||||||
|
|
||||||
1. **Gateway** — fronts all surfaces (dashboard + every per-agent UI)
|
1. **Gateway** — fronts all surfaces (dashboard + every per-agent UI)
|
||||||
on one origin. An nginx nixos-container proxies per-agent UIs under
|
on one origin. An nginx nixos-container proxies per-agent UIs under
|
||||||
`/agent/<name>/`, which is what lets the inline-answer POST to
|
`/agent/<name>/`, which is what lets each agent page's inbox panel
|
||||||
`/answer-question/{id}` go same-origin instead of needing a
|
POST `mark-all-read` to the core dashboard's
|
||||||
cross-origin CORS shim. Pure ergonomics — no behavioural risk on
|
`/api/agent/{name}/mark-all-read` go same-origin instead of needing
|
||||||
|
a cross-origin CORS shim. Pure ergonomics — no behavioural risk on
|
||||||
its own.
|
its own.
|
||||||
2. **Network isolation** — the load-bearing layer: every agent
|
2. **Network isolation** — the load-bearing layer: every agent
|
||||||
container runs in a private netns behind the hive bridge, always
|
container runs in a private netns behind the hive bridge, always
|
||||||
|
|
|
||||||
|
|
@ -174,14 +174,16 @@ the dashboard's "mark all read" (unbounded, per-agent).
|
||||||
### Question routing (Ask / Answer)
|
### Question routing (Ask / Answer)
|
||||||
|
|
||||||
`AgentRequest::Ask` (and the manager-flavour mirror) surfaces a
|
`AgentRequest::Ask` (and the manager-flavour mirror) surfaces a
|
||||||
structured question that either lands in the operator's dashboard
|
structured question that either lands in the operator-question queue
|
||||||
queue or in a peer agent's inbox. The recipient is the `to` field:
|
or in a peer agent's inbox. The recipient is the `to` field:
|
||||||
|
|
||||||
- `to = None` or `to = Some("operator")` — routes to the
|
- `to = None` or `to = Some("operator")` — routes to the
|
||||||
operator-question queue. The dashboard renders the question with
|
operator-question queue. ⚠️ The dashboard's whole ask/answer
|
||||||
any `options` as a chip strip plus a free-text fallback (`Other…`)
|
surface (the UI that used to render this queue and let the operator
|
||||||
so the operator is never trapped by an incomplete list. The
|
reply) has been removed — an operator-targeted question currently
|
||||||
legacy `AskOperator` variant collapses into this case.
|
has no reader anywhere; this routing case is being removed too as
|
||||||
|
the same effort continues. The legacy `AskOperator` variant
|
||||||
|
collapses into this case.
|
||||||
- `to = Some(<agent>)` — peer Q&A. The target agent receives a
|
- `to = Some(<agent>)` — peer Q&A. The target agent receives a
|
||||||
`HelperEvent::QuestionAsked { id, asker, question, options, multi }`
|
`HelperEvent::QuestionAsked { id, asker, question, options, multi }`
|
||||||
in their inbox. They reply via `AgentRequest::Answer` (or
|
in their inbox. They reply via `AgentRequest::Answer` (or
|
||||||
|
|
@ -191,21 +193,23 @@ queue or in a peer agent's inbox. The recipient is the `to` field:
|
||||||
|
|
||||||
Shape fields are uniform across both targets:
|
Shape fields are uniform across both targets:
|
||||||
|
|
||||||
- `options` is advisory — the dashboard chips are decoration over a
|
- `options` is advisory — a list is never enforced; peer-agent
|
||||||
free-text fallback; peer-agent recipients see the list in their
|
recipients see it in their `QuestionAsked` event and can return any
|
||||||
`QuestionAsked` event and can return any string.
|
string regardless.
|
||||||
- `multi = true` lets the answerer pick multiple options (checkboxes
|
- `multi = true` is a hint that the answerer should treat `options` as
|
||||||
in the dashboard, a hint in the peer-agent event). The answer
|
a multi-select rather than pick-one. The answer comes back as a
|
||||||
comes back as a single string with selections joined by `", "`.
|
single string with selections joined by `", "`.
|
||||||
- `ttl_seconds` auto-cancels with answer `[expired]` (and `answerer:
|
- `ttl_seconds` auto-cancels with answer `[expired]` (and `answerer:
|
||||||
"ttl-watchdog"`) when the wait becomes moot. `None` = wait
|
"ttl-watchdog"`) when the wait becomes moot. `None` = wait
|
||||||
indefinitely or until manual cancel.
|
indefinitely or until manual cancel.
|
||||||
|
|
||||||
Response shape is always `QuestionQueued { id }` — the asker stores
|
Response shape is always `QuestionQueued { id }` — the asker stores
|
||||||
the id and correlates the asynchronous answer event when it lands.
|
the id and correlates the asynchronous answer event when it lands.
|
||||||
Authorisation on `Answer`: only the question's `target` agent (or
|
Authorisation on `Answer`: only the question's `target` agent, or the
|
||||||
the operator via the dashboard) is permitted to reply; an answer
|
operator, is permitted to reply; an answer attempt from anyone else
|
||||||
attempt from anyone else fails the wire-side check.
|
fails the wire-side check. (The operator's only path to invoke this
|
||||||
|
was the now-removed dashboard — see the note above; the
|
||||||
|
authorisation rule itself is unchanged.)
|
||||||
|
|
||||||
### Loose-ends wire shape
|
### Loose-ends wire shape
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1172,13 +1172,6 @@ below — some endpoints aren't in it yet.
|
||||||
- `POST /api/{rebuild,kill,restart,start,destroy}/{name}` — lifecycle.
|
- `POST /api/{rebuild,kill,restart,start,destroy}/{name}` — lifecycle.
|
||||||
`destroy` accepts `purge=on` to also wipe state dirs.
|
`destroy` accepts `purge=on` to also wipe state dirs.
|
||||||
- `POST /api/purge-tombstone/{name}` — wipe a tombstone's state dirs.
|
- `POST /api/purge-tombstone/{name}` — wipe a tombstone's state dirs.
|
||||||
- `POST /api/answer-question/{id}` — answer a pending operator question.
|
|
||||||
No dashboard UI calls this any more (the questions pane is gone —
|
|
||||||
see hyperhive#3721); kept as long as the backend `ask`/`answer`
|
|
||||||
mechanism itself still exists.
|
|
||||||
- `POST /api/cancel-question/{id}` — cancel a pending question with
|
|
||||||
the sentinel `[cancelled]`. Same code path as a real answer. Same
|
|
||||||
no-UI-consumer note as above.
|
|
||||||
- `POST /api/request-spawn` — queue a Spawn approval.
|
- `POST /api/request-spawn` — queue a Spawn approval.
|
||||||
- `POST /api/update-all` — rebuild every stale container.
|
- `POST /api/update-all` — rebuild every stale container.
|
||||||
- `POST /api/rebuild-queue/{id}/cancel` — drop a `Queued` entry.
|
- `POST /api/rebuild-queue/{id}/cancel` — drop a `Queued` entry.
|
||||||
|
|
@ -1383,12 +1376,6 @@ payload):
|
||||||
sha_short, status, resolved_at, note, description) — pending
|
sha_short, status, resolved_at, note, description) — pending
|
||||||
queue + history mutations. Client mutates a derived store and
|
queue + history mutations. Client mutates a derived store and
|
||||||
re-renders only the approvals section.
|
re-renders only the approvals section.
|
||||||
- `question_added` (id, asker, question, options, multi,
|
|
||||||
asked_at, deadline_at, target) / `question_resolved` (id,
|
|
||||||
answer, answerer, answered_at, cancelled, target) — still
|
|
||||||
fired by the backend `ask`/`answer` mechanism, but the
|
|
||||||
dashboard no longer subscribes to either kind: the questions
|
|
||||||
pane that used to surface them is gone (hyperhive#3721).
|
|
||||||
- `transient_set` (name, transient_kind, since_unix) /
|
- `transient_set` (name, transient_kind, since_unix) /
|
||||||
`transient_cleared` (name) — lifecycle action spinners. The
|
`transient_cleared` (name) — lifecycle action spinners. The
|
||||||
client ticks the elapsed-seconds badge off `since_unix`
|
client ticks the elapsed-seconds badge off `since_unix`
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,12 @@
|
||||||
//! and no longer sourced from here (loose-ends-v2's questions phase) —
|
//! and no longer sourced from here (loose-ends-v2's questions phase) —
|
||||||
//! c0re remains the `Ask`/`Answer` routing + delivery rendezvous
|
//! c0re remains the `Ask`/`Answer` routing + delivery rendezvous
|
||||||
//! (`coord.questions`), it just isn't asked for the *pending-view*
|
//! (`coord.questions`), it just isn't asked for the *pending-view*
|
||||||
//! rendering anymore. The
|
//! rendering anymore. ⚠️ The operator dashboard no longer has any
|
||||||
//! operator dashboard's questions pane is unaffected: it reads
|
//! question-pending view either — its whole questions pane, the
|
||||||
//! `coord.questions.pending_all()` directly (`dashboard/state_snapshot.rs`),
|
//! `pending_all()`/`recent_answered_all()` reads that fed it, and the
|
||||||
//! independent of this module.
|
//! `/api/answer-question`/`/api/cancel-question` endpoints are gone
|
||||||
|
//! (removed along with the rest of the dashboard's ask/answer surface).
|
||||||
|
//! An operator-targeted `ask()` now has no reader anywhere.
|
||||||
//!
|
//!
|
||||||
//! Call frequency is low (an agent doing self-introspection between
|
//! Call frequency is low (an agent doing self-introspection between
|
||||||
//! turns), so the sweep happens fresh every time — no caching, no
|
//! turns), so the sweep happens fresh every time — no caching, no
|
||||||
|
|
|
||||||
|
|
@ -105,13 +105,15 @@ impl OperatorQuestions {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark a pending question answered. `answerer` is who's actually
|
/// Mark a pending question answered. `answerer` is who's actually
|
||||||
/// answering: `"operator"` for the dashboard path, or an agent's
|
/// answering: `"operator"`, or an agent's own name when responding
|
||||||
/// own name when responding via `Answer`. Authorisation:
|
/// via `Answer`. Authorisation:
|
||||||
///
|
///
|
||||||
/// - Operator-targeted questions (`target IS NULL`) can only be
|
/// - Operator-targeted questions (`target IS NULL`) can only be
|
||||||
/// answered by `"operator"`. (Agents must not be able to spoof
|
/// answered by `"operator"`. (Agents must not be able to spoof
|
||||||
/// answers to operator questions — the dashboard is the
|
/// answers to operator questions — though as of the dashboard's
|
||||||
/// privileged path.)
|
/// ask/answer surface being removed, nothing currently calls
|
||||||
|
/// this with `answerer = "operator"` for a `target IS NULL` row
|
||||||
|
/// at all; the check stays as a guard, not a live path.)
|
||||||
/// - Agent-targeted questions can only be answered by the
|
/// - Agent-targeted questions can only be answered by the
|
||||||
/// declared target agent, OR by `"operator"` (operator override
|
/// declared target agent, OR by `"operator"` (operator override
|
||||||
/// for stuck threads — useful when an agent is offline/down
|
/// for stuck threads — useful when an agent is offline/down
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue