feat: topic-config restore (#136), Phase 1 header preflight (#137), evidence contract v2 (#138) - #141
Open
sionsmith wants to merge 1 commit into
Open
feat: topic-config restore (#136), Phase 1 header preflight (#137), evidence contract v2 (#138)#141sionsmith wants to merge 1 commit into
sionsmith wants to merge 1 commit into
Conversation
…ct v2 Cyber-recovery hardening stack for the restore/validation path, closing three issues that were implemented and verified together: Issue #136 — topic configuration capture and restore: - Backup captures allowlisted, mutable topic configuration overrides and replication-factor metadata into the manifest. - Restore applies captured configuration to created topics, with policies for pre-existing targets, per-topic overrides, and drift reporting. - Supporting Kafka client, partition-router, storage, and TLS fixes. Issue #137 — real Phase 1 header preflight (replaces the placeholder ThreePhaseRestore::validate_phase1_headers): - New restore/preflight.rs scans every selected topic/partition/segment (binary + legacy JSON formats) and reports structured coverage states: full/partial/missing/empty/data_missing/corrupt/indeterminate. Zero records scanned is never a positive pass. - Consumer-offset recovery (reset_consumer_offsets/auto_consumer_groups) fails BEFORE the engine connects to the target — before topic creation, config restore, purge, or produce — when required tracking metadata (x-original-offset/x-original-timestamp headers, consumer-groups snapshot) is missing, partial, corrupt, or unprovable. - Wired into RestoreEngine::run, ThreePhaseRestore::run_all_phases (structured result in ThreePhaseReport.header_preflight), validate_phase1_headers, dry_run/validate-restore, and the CLI. - Modes: restore.header_preflight: auto|full|skip (skip = explicit escape hatch restoring pre-preflight behaviour with loud warnings). - Docs: docs/restore-preflight.md. Tests: preflight_contract_test (17), Docker E2E preflight_e2e (2) proving a failed preflight leaves the target unchanged. Issue #138 — versioned byte-level evidence contract (v2): - Report schema 1.1 never embeds its own digest; report_sha256 is a legacy-read-only field. The digest and ECDSA P-256 signature live in a detached envelope (kafka-backup/evidence-envelope/v2) computed as a DSSE v1 signature over PAE(payloadType, exact stored report bytes). - Verification hashes stored bytes as read — no re-serialization or canonicalization — and supports both v2 envelopes and legacy v1 text .sig artifacts (including ones produced by the original blank-digest bug). Unknown envelope schemas are rejected explicitly. - Artifact creation/verification centralized in evidence::emit / evidence::envelope; the CLI is a thin wrapper, and the enterprise distribution consumes the same functions so the binaries cannot drift. - to_canonical_json renamed to_deterministic_json; documented and unit-proven NOT RFC 8785 (divergences pinned by test). - Docs: docs/evidence-contract.md (contract, citations to RFC 8785 and DSSE/in-toto, deliberate deviations). Tests: evidence_contract_test (10) covering reload/recompute/verify, tampered JSON, tampered envelope, wrong key, legacy evidence, and PDF/CLI digest consistency. Verified locally: cargo test --workspace 421 passed / 0 failed / 91 ignored; fmt and clippy clean; cargo audit no vulnerabilities; Docker suites preflight_e2e 2/2, validation evidence 3/3, restore validation 2/2, issue_67 10/10, offset_semantics 4/4; release build OK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Cyber-recovery hardening for the restore/validation path. One integrated, fully verified commit closing three issues (per-issue boundaries run through shared files —
config.rs,manifest.rs,restore/engine.rs— so splitting would have produced non-building intermediate commits).Closes #136, closes #137, closes #138.
#136 — topic configuration capture and restore
existing_topic_config_policyfor pre-existing targets, per-topic overrides, and drift reporting.#137 — real Phase 1 header preflight
Replaces the placeholder
ThreePhaseRestore::validate_phase1_headers(always returned success withsample_records_checked: 0, no production call sites).restore/preflight.rs: scans every selected topic/partition/segment (binary + legacy JSON formats, honoring partition/PITR filters); structured per-partition statesfull/partial/missing/empty/data_missing/corrupt/indeterminate; zero records is never a positive pass.reset_consumer_offsets/auto_consumer_groups) fails before the engine connects to the target — before topic creation, config restore, purge, or produce — when required tracking metadata is missing/partial/corrupt/unprovable.auto_consumer_groupsnow requires a valid consumer-groups snapshot; a reset request that could never act now fails instead of silently skipping.RestoreEngine::run,run_all_phases(ThreePhaseReport.header_preflight),validate_phase1_headers,dry_run/validate-restore(DryRunReport.header_preflight, exit 1 on blocking findings), and the CLI output.restore.header_preflight: auto|full|skip—skipis the documented emergency escape hatch (pre-PR behavior, loud UNVERIFIED warnings).docs/restore-preflight.md. Evidence: issue comment.#138 — versioned byte-level evidence contract (v2)
Eliminates the self-referential
report_sha256(stored/signed JSON carried an empty field while the PDF displayed a digest matching no stored artifact).1.1never embeds its own digest; the digest + ECDSA P-256 signature live in a detached envelope (kafka-backup/evidence-envelope/v2) computed as a DSSE v1 signature overPAE(payloadType, exact stored report bytes)(spec PAE vector pinned by test)..sigartifacts — including ones produced by the original bug — verify via an explicitly tested path; unknown schemas are rejected, never reinterpreted.evidence::emit/evidence::envelope; the CLI is a thin wrapper and the enterprise distribution consumes the same functions (cross-binary verification demonstrated).to_canonical_json→to_deterministic_json, documented and unit-proven not RFC 8785 (divergences pinned:-0.0,1e-6, UTF-16 vs UTF-8 key order, >2^53 integers).docs/evidence-contract.md(byte-level contract, RFC 8785 / DSSE / in-toto citations, deliberate deviations). Evidence: issue comment.Verification
cargo test --workspace: 421 passed / 0 failed / 91 ignored (Docker-gated); new suites:preflight_contract_test17/17,evidence_contract_test10/10.preflight_e2e2/2 (failed preflight leaves the target unchanged + mutation-detecting control), evidence 3/3, restore-validate 2/2,issue_6710/10,offset_semantics4/4.cargo fmt --checkclean;cargo clippy --workspace --all-targets0 warnings;cargo auditno vulnerabilities (3 known informational advisories); release build OK.shasumof stored bytes == envelope == PDF; cross-verified by the enterprise binary; tampered copy fails (exit 1); header-less backup refused before any target mutation.Notes for reviewers
docs/restore-preflight.md):auto_consumer_groupswith a missing/invalid snapshot andreset_consumer_offsetswith nothing to act on now fail before mutation;header_preflight: skiprestores old behavior explicitly..sigobjects are now JSON envelopes; old binaries fail to parse them explicitly (never a wrong verdict). New binaries verify both formats.ec2995a;origin/mainhas advanced since — happy to merge main into the branch if you want it current before review.🤖 Generated with Claude Code