feat(rotate-key): add report + key fingerprint in hexdigit fmt
This commit is contained in:
@@ -321,7 +321,7 @@ impl RecoverableJobHandler for BackendConsistencyCheck {
|
||||
backend = backend.backend_type(),
|
||||
"backend refused enumeration (typical during migration or on backends without list support)"
|
||||
);
|
||||
return RunOutcome::Completed;
|
||||
return RunOutcome::completed();
|
||||
}
|
||||
return RunOutcome::Failed {
|
||||
message: format!("backend list failed mid-scan: {e}"),
|
||||
@@ -373,7 +373,7 @@ impl RecoverableJobHandler for BackendConsistencyCheck {
|
||||
"backend_consistency completed with {} finding(s)",
|
||||
finding_count
|
||||
);
|
||||
return RunOutcome::Completed;
|
||||
return RunOutcome::completed();
|
||||
}
|
||||
|
||||
// Batch DB probe: which of these hashes have a
|
||||
@@ -451,7 +451,7 @@ impl RecoverableJobHandler for BackendConsistencyCheck {
|
||||
"backend_consistency completed with {} finding(s)",
|
||||
finding_count
|
||||
);
|
||||
return RunOutcome::Completed;
|
||||
return RunOutcome::completed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ impl RecoverableJobHandler for BlobsConsistencyCheck {
|
||||
"blobs_consistency completed with {} finding(s)",
|
||||
finding_count
|
||||
);
|
||||
return RunOutcome::Completed;
|
||||
return RunOutcome::completed();
|
||||
}
|
||||
|
||||
let grace_cutoff = Utc::now() - CREATE_GRACE;
|
||||
@@ -528,7 +528,7 @@ impl RecoverableJobHandler for BlobsConsistencyCheck {
|
||||
"blobs_consistency completed with {} finding(s)",
|
||||
finding_count
|
||||
);
|
||||
return RunOutcome::Completed;
|
||||
return RunOutcome::completed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +199,7 @@ impl RecoverableJobHandler for DrivesConsistencyCheck {
|
||||
"drives_consistency completed with {} drift finding(s)",
|
||||
drift_count
|
||||
);
|
||||
return RunOutcome::Completed;
|
||||
return RunOutcome::completed();
|
||||
}
|
||||
|
||||
// Per-row check: cached vs actual. This is the ONE check
|
||||
@@ -255,7 +255,7 @@ impl RecoverableJobHandler for DrivesConsistencyCheck {
|
||||
"drives_consistency completed with {} drift finding(s)",
|
||||
drift_count
|
||||
);
|
||||
return RunOutcome::Completed;
|
||||
return RunOutcome::completed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,6 +313,35 @@ impl BlobFormat {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for BlobFormat {
|
||||
/// Human-friendly format for audit logs + finding details.
|
||||
/// Renders `key_fp` as SSH-style colon-hex (e.g.
|
||||
/// `83:96:ff:90:94:d7:ef:de`) instead of the raw byte-array Debug
|
||||
/// shape (`[131, 150, 255, ...]`). Same spelling `xxd` produces
|
||||
/// when you inspect a blob's on-disk header, so operators can
|
||||
/// cross-check without a mental conversion.
|
||||
///
|
||||
/// Handlers that render this in tracing macros should use `%`
|
||||
/// (Display) — `?` (Debug) still gives the raw byte-array shape
|
||||
/// for programmer-consumers who need the exact bytes.
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
BlobFormat::Legacy => write!(f, "legacy"),
|
||||
BlobFormat::PlaintextV1 => write!(f, "plaintext-v1"),
|
||||
BlobFormat::EncryptedV1 { key_fp } => {
|
||||
write!(f, "encrypted-v1 key_fp=")?;
|
||||
for (i, byte) in key_fp.iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ":")?;
|
||||
}
|
||||
write!(f, "{byte:02x}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Assemble an encrypted-v1 blob:
|
||||
/// `OXCPT | v1 | key_fp | nonce | ciphertext | tag`.
|
||||
///
|
||||
|
||||
@@ -312,7 +312,7 @@ impl RecoverableJobHandler for FilesConsistencyCheck {
|
||||
"files_consistency completed with {} finding(s)",
|
||||
finding_count
|
||||
);
|
||||
return RunOutcome::Completed;
|
||||
return RunOutcome::completed();
|
||||
}
|
||||
|
||||
for row in &rows {
|
||||
@@ -490,7 +490,7 @@ impl RecoverableJobHandler for FilesConsistencyCheck {
|
||||
"files_consistency completed with {} finding(s)",
|
||||
finding_count
|
||||
);
|
||||
return RunOutcome::Completed;
|
||||
return RunOutcome::completed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ impl RecoverableJobHandler for FoldersConsistencyCheck {
|
||||
"folders_consistency completed with {} finding(s)",
|
||||
finding_count
|
||||
);
|
||||
return RunOutcome::Completed;
|
||||
return RunOutcome::completed();
|
||||
}
|
||||
|
||||
// Per-row branches. Add new ones here — same pattern as
|
||||
@@ -352,7 +352,7 @@ impl RecoverableJobHandler for FoldersConsistencyCheck {
|
||||
"folders_consistency completed with {} finding(s)",
|
||||
finding_count
|
||||
);
|
||||
return RunOutcome::Completed;
|
||||
return RunOutcome::completed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -799,7 +799,15 @@ impl StorageMigrationService {
|
||||
"✅ storage_migration completed — hot-swapped runtime backend to `{target_name}`, \
|
||||
writes resumed. No restart required."
|
||||
);
|
||||
RunOutcome::Completed
|
||||
// Per-run summary counters merged into `stats` for the admin
|
||||
// UI drawer. Same shape as `storage_rotate`'s extras + one
|
||||
// extra `source_missing` counter unique to migration.
|
||||
RunOutcome::completed_with(serde_json::json!({
|
||||
"copied": copied,
|
||||
"skipped": skipped,
|
||||
"failed": failed,
|
||||
"source_missing": source_missing,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -232,9 +232,13 @@ impl RecoverableJobHandler for StorageRotateService {
|
||||
event = "storage_rotate.run_started",
|
||||
run_id = %store.run_id(),
|
||||
target_name = %target_name,
|
||||
head_format = ?head_format,
|
||||
// `%` (Display) → SSH-style `encrypted-v1 key_fp=83:96:...`
|
||||
// instead of the raw `[131, 150, 255, ...]` byte-array
|
||||
// shape Debug produces. Matches how `xxd` renders the
|
||||
// header bytes on disk.
|
||||
head_format = %head_format,
|
||||
resuming = !is_fresh,
|
||||
"storage_rotate started on `{target_name}` (head_format = {head_format:?})"
|
||||
"storage_rotate started on `{target_name}` (head_format = {head_format})"
|
||||
);
|
||||
|
||||
// Seed the progress snapshot. Total = count_total's estimate;
|
||||
@@ -411,8 +415,8 @@ impl RecoverableJobHandler for StorageRotateService {
|
||||
serde_json::json!({
|
||||
"hash": hash,
|
||||
"phase": "write",
|
||||
"from": format!("{current_format:?}"),
|
||||
"to": format!("{head_format:?}"),
|
||||
"from": format!("{current_format}"),
|
||||
"to": format!("{head_format}"),
|
||||
"error": e.to_string(),
|
||||
}),
|
||||
)
|
||||
@@ -484,7 +488,16 @@ impl StorageRotateService {
|
||||
failed = failed,
|
||||
"storage_rotate completed on `{target_name}` — {rewritten} rewritten, {skipped} skipped, {failed} failed"
|
||||
);
|
||||
RunOutcome::Completed
|
||||
// Surface the per-run summary counters as extras merged into
|
||||
// the run row's `stats` JSONB. Frontend renders whatever keys
|
||||
// are present, so no wire-format bumping is needed — the
|
||||
// admin UI's run drawer just picks these up alongside the
|
||||
// engine-owned `finding_count` + `scanned_count`.
|
||||
RunOutcome::completed_with(serde_json::json!({
|
||||
"rewritten": rewritten,
|
||||
"skipped": skipped,
|
||||
"failed": failed,
|
||||
}))
|
||||
}
|
||||
|
||||
fn clear_progress(&self) {
|
||||
|
||||
Reference in New Issue
Block a user