controller v0.195.0: prove the offsite key comes back (R-200 plumbing half) -- MinAgent 0.125.0
gates / gates (push) Successful in 10s
gates / gates (push) Successful in 10s
--recover-offsite-check is a docker exec diagnostic in the shape of --print-reset-code: it reads the customer's recovery code from STDIN, asks the agent to fetch this host's sealed bundle and open it, and reports whether the recovered key matches the one on disk BY SHA256. Two hashes and a verdict; never a password, never R, never a blob. R comes from stdin and not a flag because a flag value is visible in ps, in shell history, in a container's command line and in any transcript of the session that ran it. IT COMPARES; IT DOES NOT INSTALL. The recovered password is never written to offbox/repo_password -- installing changes a live box on a path nobody has walked, and that link is next session's, with the drill around it. A test asserts the data dir is byte-unchanged after a check; its red-proof (adding the install call) fails it. Exit codes: 0 match, 2 clean MISMATCH, 1 a step failed -- "it failed" and "it worked and disagreed" must never share a status. A box with no local password reports distinctly: that is the rebuilt-box shape, where the next step is to install rather than compare. Nothing customer-reachable ships here: no card, no form, no preview.
This commit is contained in:
@@ -117,3 +117,33 @@ func (c *Client) EscrowCeremonyClaim(ctx context.Context) (string, int, error) {
|
||||
}
|
||||
return out.RecoveryCode, status, nil
|
||||
}
|
||||
|
||||
// RecoverOffsiteRepoPassword asks the agent to open this host's hub-held sealed bundle with the
|
||||
// customer's recovery code and return ONLY the offsite restic repository password, plus its sha256
|
||||
// (R-199, agent >= v0.125.0).
|
||||
//
|
||||
// R CROSSES HERE, AND NOWHERE ELSE IN THIS DIRECTION. It travels in the request body over the pinned
|
||||
// local-API channel (the operator's 2026-08-04 acceptance) and is not retained by this client. The
|
||||
// shared POST helper logs path/status/duration and never bodies — do not add a body log, on either
|
||||
// the request or the response side: the request carries R and the response carries the password.
|
||||
func (c *Client) RecoverOffsiteRepoPassword(ctx context.Context, recoveryCode string) (password, sha256hex string, err error) {
|
||||
env, status, perr := c.postWithStatus(ctx, "/escrow/recover-offsite-password",
|
||||
map[string]string{"recovery_code": recoveryCode})
|
||||
if perr != nil {
|
||||
return "", "", perr
|
||||
}
|
||||
if rerr := refusalError("/escrow/recover-offsite-password", status, env); rerr != nil {
|
||||
return "", "", rerr
|
||||
}
|
||||
var out struct {
|
||||
ResticRepoPassword string `json:"restic_repo_password"`
|
||||
ResticPwSHA256 string `json:"restic_pw_sha256"`
|
||||
}
|
||||
if uerr := json.Unmarshal(env.Data, &out); uerr != nil {
|
||||
return "", "", fmt.Errorf("agentapi: decode /escrow/recover-offsite-password: %w", uerr)
|
||||
}
|
||||
if out.ResticRepoPassword == "" || out.ResticPwSHA256 == "" {
|
||||
return "", "", fmt.Errorf("agentapi: the agent returned an empty recovery result")
|
||||
}
|
||||
return out.ResticRepoPassword, out.ResticPwSHA256, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user