feat(server): operator-triggered chunked agent update push (VS-73) #91

Merged
David merged 1 commit from feat/VS-73-manual-agent-update-push into main 2026-06-30 23:31:03 +02:00
Owner

What

Implements VS-73: an operator-triggered manual agent update that chunk-streams the configured-channel binary over the already-authenticated mTLS control channel. This is the chosen design; the existing out-of-band signed-URL updateAgents push stays as the recovery alternative.

New control actions (site-admin gated)

  • pushAgentUpdate { nodeids }: for each connected agent, resolve its AgentId (live registry value, falling back to the persisted node.agent.id), select the matching binary from the fetched-and-verified catalog (VS-20; signature verified at fetch time), and stream AgentUpdate Begin / AgentUpdateBlock chunks / AgentUpdate End via the existing build_agent_update_frames. The agent validates the assembled bytes against the End-frame SHA-384 before swapping and re-exec'ing. Agents already reporting the channel binary's hash are skipped. There is no per-push version/target field: the push always serves the configured channel binary (AC6). The per-node outcome (pushed / current / offline / nobinary) is reported back (AC5). When the catalog has no entry for the target arch and a fetcher is configured, one on-demand refresh cycle runs before giving up.
  • agentUpdateStatus { nodeids }: read-only pre-push visibility (AC5). Reports per node: connected state, AgentId, running binary hash, reported commit date, the configured channel binary hash, and a behind flag, plus the channel label (e.g. forgejo:owner/pkg@stable).

Acceptance criteria

  • AC1: operator can trigger a manual agent update for a selected connected agent (pushAgentUpdate).
  • AC2: server uses the configured-channel binary fetched + signature-verified by the VS-20 fetcher (catalog populated by the refresher; on-demand refresh if missing).
  • AC3: server selects the correct per-platform artifact for the target agent's AgentId.
  • AC4: push uses the chunked AgentUpdate / AgentUpdateBlock protocol with the End-frame SHA-384; per-block acks are consumed by the existing dispatch path (same as the proven reactive push).
  • AC5: operator sees each agent's running build vs the channel (agentUpdateStatus) and the push outcome is reported.
  • AC6: no per-push version/target field; the push serves the configured channel binary.

Note on reported semver (AC5)

The agent reports a commit date today, not a semver; the reported-semver field is the linked VA dependency. Until it lands, agentUpdateStatus uses the running binary SHA-384 as the version proxy that drives behind, with commitdate surfaced alongside. The behind flag and channel comparison are already correct on the hash, so adding semver later is additive.

Tests

Five new unit tests cover the push outcomes (offline / streamed-chunked / already-current / no-binary) and the status behind/current comparison. Full suite green via just pre-commit (fmt, clippy -D warnings, check, test --lib).

## What Implements VS-73: an operator-triggered manual agent update that chunk-streams the configured-channel binary over the already-authenticated mTLS control channel. This is the chosen design; the existing out-of-band signed-URL `updateAgents` push stays as the recovery alternative. ## New control actions (site-admin gated) - `pushAgentUpdate { nodeids }`: for each connected agent, resolve its `AgentId` (live registry value, falling back to the persisted `node.agent.id`), select the matching binary from the fetched-and-verified catalog (VS-20; signature verified at fetch time), and stream `AgentUpdate` Begin / `AgentUpdateBlock` chunks / `AgentUpdate` End via the existing `build_agent_update_frames`. The agent validates the assembled bytes against the End-frame SHA-384 before swapping and re-exec'ing. Agents already reporting the channel binary's hash are skipped. There is no per-push version/target field: the push always serves the configured channel binary (AC6). The per-node outcome (`pushed` / `current` / `offline` / `nobinary`) is reported back (AC5). When the catalog has no entry for the target arch and a fetcher is configured, one on-demand refresh cycle runs before giving up. - `agentUpdateStatus { nodeids }`: read-only pre-push visibility (AC5). Reports per node: connected state, `AgentId`, running binary hash, reported commit date, the configured channel binary hash, and a `behind` flag, plus the channel label (e.g. `forgejo:owner/pkg@stable`). ## Acceptance criteria - [x] AC1: operator can trigger a manual agent update for a selected connected agent (`pushAgentUpdate`). - [x] AC2: server uses the configured-channel binary fetched + signature-verified by the VS-20 fetcher (catalog populated by the refresher; on-demand refresh if missing). - [x] AC3: server selects the correct per-platform artifact for the target agent's `AgentId`. - [x] AC4: push uses the chunked `AgentUpdate` / `AgentUpdateBlock` protocol with the End-frame SHA-384; per-block acks are consumed by the existing dispatch path (same as the proven reactive push). - [x] AC5: operator sees each agent's running build vs the channel (`agentUpdateStatus`) and the push outcome is reported. - [x] AC6: no per-push version/target field; the push serves the configured channel binary. ## Note on reported semver (AC5) The agent reports a commit date today, not a semver; the reported-semver field is the linked VA dependency. Until it lands, `agentUpdateStatus` uses the running binary SHA-384 as the version proxy that drives `behind`, with `commitdate` surfaced alongside. The `behind` flag and channel comparison are already correct on the hash, so adding semver later is additive. ## Tests Five new unit tests cover the push outcomes (offline / streamed-chunked / already-current / no-binary) and the status `behind`/`current` comparison. Full suite green via `just pre-commit` (fmt, clippy -D warnings, check, test --lib).
feat(server): operator-triggered chunked agent update push (VS-73)
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 2m10s
Create release / Create release from merged PR (pull_request) Has been skipped
b8a81166fb
Add a manual, operator-triggered agent-update path that chunk-streams the configured-channel binary over the already-authenticated mTLS control channel, the chosen design over the out-of-band signed-URL `updateAgents` push (which stays as the recovery alternative).

New site-admin-gated control actions:

- `pushAgentUpdate { nodeids }`: for each connected agent, resolve its `AgentId` (live registry value, falling back to the persisted `node.agent.id`), pick the matching binary from the fetched-and-verified catalog (VS-20; signature verified at fetch time), and stream `AgentUpdate` Begin / `AgentUpdateBlock` chunks / `AgentUpdate` End via the existing `build_agent_update_frames`. The agent validates the assembled bytes against the End-frame SHA-384 before swapping and re-exec'ing. Agents already reporting the channel binary's hash are skipped. No per-push version/target field: the push always serves the configured channel binary (AC6). The per-node outcome (pushed / current / offline / nobinary) is reported back (AC5). When the catalog has no entry for the target arch and a fetcher is configured, one on-demand refresh cycle runs before giving up.
- `agentUpdateStatus { nodeids }`: read-only pre-push visibility. Reports per node connected state, AgentId, running binary hash, reported commit date, the configured channel binary hash, and a `behind` flag, plus the channel label (e.g. `forgejo:owner/pkg@stable`). The reported semver field is added once the agent reports it (linked VA issue); today the running binary SHA-384 is the version proxy that drives `behind`.

Add `AgentBinaryRefresher::source_label()` to surface the configured channel for the status reply.

#VS-73

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
David scheduled this pull request to auto merge when all checks succeed 2026-06-30 23:30:57 +02:00
David merged commit e7b6c89069 into main 2026-06-30 23:31:03 +02:00
David deleted branch feat/VS-73-manual-agent-update-push 2026-06-30 23:31:03 +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!91
No description provided.