v0.107.0: key-auth-first bridge + staged-secret wipe on escrow confirm

Key-auth-first: a KeyAuthProber seam lets the bridge skip consume+install
when the already-installed key still authenticates (pinned to the freshly
verified host key) — descriptor changes on provisioned guests no longer
loop on consume-404. Fingerprint verify still precedes everything.

Wipe-on-escrowed: confirm-escrow now calls the agent's new
DELETE /escrow/stage-secret (v0.78.0) best-effort, closing the hygiene gap
where a ceremony-less confirm left the staged password file behind.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-09 22:26:53 +02:00
parent 42af088308
commit a38c743926
9 changed files with 241 additions and 1 deletions
+23
View File
@@ -474,6 +474,29 @@ func (c *Client) StageEscrowSecret(ctx context.Context, resticRepoPassword strin
return refusalError("/escrow/stage-secret", status, env)
}
// WipeStagedEscrowSecret removes the agent-staged offsite repo password (fork-4 hygiene) — called whenever
// EscrowState flips to escrowed, so the transient 0600 staging file doesn't outlive its purpose. Idempotent
// on the agent side (an absent file is a clean 200). Requires agent >= v0.78.0 (older agents 404 — the
// caller logs loudly and moves on).
func (c *Client) WipeStagedEscrowSecret(ctx context.Context) error {
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, c.baseURL+"/escrow/stage-secret", nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+c.token)
resp, err := c.hc.Do(req)
if err != nil {
return fmt.Errorf("agentapi: DELETE /escrow/stage-secret: %w", err)
}
defer resp.Body.Close()
raw, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
var env apiResponse
if err := json.Unmarshal(raw, &env); err != nil {
return fmt.Errorf("agentapi: DELETE /escrow/stage-secret: HTTP %d, bad envelope: %w", resp.StatusCode, err)
}
return refusalError("/escrow/stage-secret", resp.StatusCode, env)
}
func (c *Client) EjectDisk(ctx context.Context, where string) (EjectResult, error) {
var out EjectResult
env, status, err := c.postWithStatus(ctx, "/disks/eject", map[string]string{"where": where})