R-105 (decision 169): retire the never-built slim DR record fields (no writer, no reader); 05 §9/§11, 06 §3.5 corrected

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-07 10:02:19 +02:00
parent e7fb10200e
commit aeca52ea54
12 changed files with 121 additions and 63 deletions
+7 -12
View File
@@ -1220,9 +1220,9 @@ type escrowUploadRequest struct {
KeyFingerprint string `json:"key_fingerprint"` // for operator display only
Posture string `json:"posture"` // e.g. "zero_knowledge"
CreatedAt string `json:"created_at"` // RFC3339
// Slice 10D.1 — optional DR bundle, stored alongside the K-escrow (both opaque/non-secret).
IdentityBlobB64 string `json:"identity_blob_b64,omitempty"` // age-wrapped {tunnel_token, pbs_token}
DirectiveJSON json.RawMessage `json:"directive,omitempty"` // non-secret directive (pbs repo/ns, expected fp, tunnel id)
// Slice 10D.1 — optional identity escrow, stored alongside the K-escrow (opaque). An old agent may still send a
// `directive`; it is ignored (R-105, decision 169 — retired, never read).
IdentityBlobB64 string `json:"identity_blob_b64,omitempty"` // age-wrapped {tunnel_token, pbs_token}
// SLICE 3 — sha256 hex of the restic repo password sealed in the identity blob (non-reversible hash
// of a 256-bit random secret — safe to store/serve; present only when a staged password was folded in).
ResticPwSHA256 string `json:"restic_pw_sha256,omitempty"`
@@ -1290,25 +1290,20 @@ func (h *Handler) handleHostEscrowPut(w http.ResponseWriter, r *http.Request, pa
h.maybeEmitRepoKeyChanged(host.CustomerID, pathHostID, prevPwSHA, req.ResticPwSHA256, n)
}
}
// Slice 10D.1: optionally store the IDENTITY escrow blob + the non-secret DR directive alongside
// the K-escrow (both opaque / non-secret — no usable secret hub-side). Additive: a slice-7
// upload without these is unchanged.
// Slice 10D.1: optionally store the IDENTITY escrow blob alongside the K-escrow (opaque — no usable secret
// hub-side). Additive: a slice-7 upload without it is unchanged. The directive is retired (R-105).
if req.IdentityBlobB64 != "" {
idBlob, derr := base64.StdEncoding.DecodeString(req.IdentityBlobB64)
if derr != nil || len(idBlob) == 0 {
http.Error(w, "Invalid payload: identity_blob_b64 not valid base64", http.StatusBadRequest)
return
}
directive := req.DirectiveJSON
if len(directive) == 0 || !json.Valid(directive) {
directive = json.RawMessage("{}")
}
if err := h.store.SaveHostDRBundle(pathHostID, idBlob, string(directive)); err != nil {
if err := h.store.SaveHostDRBundle(pathHostID, idBlob); err != nil {
h.logger.Printf("[ERROR] Failed to store DR bundle for host %s: %v", pathHostID, err)
http.Error(w, "Internal error", http.StatusInternalServerError)
return
}
h.logger.Printf("[INFO] stored DR bundle for host %s (identity %d bytes + directive)", pathHostID, len(idBlob))
h.logger.Printf("[INFO] stored DR bundle for host %s (identity %d bytes)", pathHostID, len(idBlob))
}
h.logger.Printf("[INFO] stored opaque escrow blob for host %s (%d bytes, posture=%s, fp=%s)",
pathHostID, len(blob), req.Posture, req.KeyFingerprint)