approve_node never rotates an existing key; node update-check endpoint; real Update Node button (node-26#14, #15)
- approve_node: reuses node_keys/{id}.api_key when one exists (still
re-syncs dynsec with it, still never overwrites node_keys). Re-clicking
Approve can no longer lock a live node out. Reissue stays explicit.
- POST /nodes/{id}/request-update (admin): sets update_requested_at on the
node doc AND sends MQTT node_update best-effort, so offline/locked-out
nodes still get it.
- POST /nodes/{id}/update-check (node, inline auth: Bearer api_key for THAT
node or X-Pickup-Secret): host updater's HTTPS check-in. Returns only
update_requested_at; records updater_last_check_at / sha / applied result.
Unknown node -> 401 (no node_id oracle). C2 cannot name a ref, image or
command.
- Frontend: "Update Node Software" (admin only) calls request-update, honest
confirm text; node page shows updater check-in and last update state.
Tests: tests/test_node_provisioning.py (12).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
fb299008f8
commit
7fa50f24d2
@@ -65,6 +65,12 @@ export const c2api = {
|
||||
// Node approval
|
||||
approveNode: (id: string) =>
|
||||
request(`/nodes/${id}/approve`, { method: "POST" }),
|
||||
/** node-26#15: flag the node for its host updater (HTTPS) and nudge it over MQTT. */
|
||||
requestNodeUpdate: (id: string) =>
|
||||
request<{ ok: boolean; update_requested_at: string; mqtt_sent: boolean }>(
|
||||
`/nodes/${id}/request-update`,
|
||||
{ method: "POST" },
|
||||
),
|
||||
rejectNode: (id: string) =>
|
||||
request(`/nodes/${id}/reject`, { method: "POST" }),
|
||||
deleteNode: (id: string) =>
|
||||
|
||||
Reference in New Issue
Block a user