Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@
WAL projection graph material, accepted-submission evidence, and receipt
correlation evidence while reporting external segment bytes as explicit
dependencies and normalizing absolute locator paths out of causal identity.
- `warp-core` now exports and validates a self-contained WAL WSC fixture that
embeds WAL segment bytes as WSC material, replays those bytes through WAL
recovery to validate segment digest and commit-chain evidence, rebuilds
accepted-submission and receipt indexes without access to the original
filesystem WAL root, and reports tampered embedded bytes as typed recovery
obstruction evidence.
- `cargo xtask test-slice durable-runtime-wal` now runs the release-grade
filesystem runtime WAL durability gate, joining filesystem ACK recovery,
filesystem failure atomicity, CLI submission posture JSON, stale-claim, and
Expand Down
59 changes: 59 additions & 0 deletions crates/warp-core/src/causal_wal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1727,6 +1727,17 @@ pub struct WalRecoveredTransaction {
pub frames: Vec<WalFrame>,
}

/// Recovery evidence decoded from embedded WAL segment bytes.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WalSegmentBytesRecovery {
/// Logical segment id.
pub segment_id: WalSegmentId,
/// Digest computed from recovered segment frames.
pub segment_digest: Hash,
/// Recovery report built from segment frames and commit markers.
pub report: RecoveryScanReport,
}

/// WAL submission acceptance record payload.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SubmissionAcceptanceRecord {
Expand Down Expand Up @@ -4388,6 +4399,48 @@ pub fn recover_filesystem_store(
Ok(report)
}

/// Recovers committed transactions from embedded bytes for one WAL segment.
///
/// This validates the same disk record checksums, frame integrity, transaction
/// commit chain, and read-only tail posture used by filesystem WAL recovery,
/// but it does not require access to the original filesystem root.
pub fn recover_wal_segment_bytes(
segment_id: WalSegmentId,
bytes: &[u8],
mode: RecoveryAccessMode,
) -> Result<WalSegmentBytesRecovery, WalRecoveryError> {
let (frames, commits, torn_tail) = read_segment_bytes(bytes)?;
for frame in &frames {
if frame.header.segment_id != segment_id {
return Err(WalStoreError::SegmentMismatch {
expected: segment_id,
actual: frame.header.segment_id,
}
.into());
}
}
let frame_refs = frames.iter().collect::<Vec<_>>();
let segment_digest = segment_digest(segment_id, &frame_refs);
let mut report = recover_from_frames_and_commits(&frames, &commits, mode)?;
if torn_tail && matches!(report.tail_posture, RecoveryTailPosture::Clean) {
report.tail_posture = match mode {
RecoveryAccessMode::Writable => report.last_committed_lsn().map_or(
RecoveryTailPosture::TruncatedAll,
RecoveryTailPosture::TruncatedAfter,
),
RecoveryAccessMode::ReadOnly => report.last_committed_lsn().map_or(
RecoveryTailPosture::WouldTruncateAll,
RecoveryTailPosture::WouldTruncateAfter,
),
};
}
Ok(WalSegmentBytesRecovery {
segment_id,
segment_digest,
report,
})
}

/// Runs a read-only WAL doctor over filesystem WAL segments.
pub fn doctor_filesystem_store(
root: impl AsRef<Path>,
Expand Down Expand Up @@ -5877,6 +5930,12 @@ fn read_segment_file(
) -> Result<(Vec<WalFrame>, Vec<WalTransactionCommit>, bool), WalStoreError> {
let mut bytes = Vec::new();
File::open(path)?.read_to_end(&mut bytes)?;
read_segment_bytes(&bytes)
}

fn read_segment_bytes(
bytes: &[u8],
) -> Result<(Vec<WalFrame>, Vec<WalTransactionCommit>, bool), WalStoreError> {
let mut offset = 0usize;
let mut frames = Vec::new();
let mut commits = Vec::new();
Expand Down
11 changes: 7 additions & 4 deletions crates/warp-core/src/wsc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,18 @@ pub use store::{
retention_records_to_wsc_envelope, topology_records_from_wsc_envelope,
topology_records_from_wsc_store, topology_records_to_wsc_envelope,
validate_wsc_causal_history_store, validate_wsc_ref_only_wal_export,
wsc_causal_history_export_profile, wsc_causal_history_export_profiles, wsc_ref_only_wal_export,
validate_wsc_self_contained_wal_export, wsc_causal_history_export_profile,
wsc_causal_history_export_profiles, wsc_ref_only_wal_export, wsc_self_contained_wal_export,
InMemoryWscStore, WscCausalHistoryCasAuthority, WscCausalHistoryExportEvidence,
WscCausalHistoryExportProfile, WscCausalHistoryExportProfileKind,
WscCausalHistoryExportValidationMaterial, WscReceiptCorrelationRecords, WscRefOnlyWalExport,
WscRefOnlyWalExportError, WscRefOnlyWalImport, WscRefOnlyWalImportError,
WscRefOnlyWalLocatorPosture, WscRefOnlyWalMaterialDependency, WscRefOnlyWalSegmentDependency,
WscRetentionRecords, WscStoreEnvelope, WscStoreEnvelopeId, WscStoreObstruction,
WscStoreObstructionKind, WscStorePort, WscStoreRecordKind, WscStoreSubject,
WscStoreWriteReceipt, WscTopologyRecords, WSC_CAUSAL_HISTORY_EXPORT_PROFILE_VERSION,
WscRetentionRecords, WscSelfContainedWalExport, WscSelfContainedWalExportError,
WscSelfContainedWalImport, WscSelfContainedWalImportError, WscSelfContainedWalSegmentMaterial,
WscStoreEnvelope, WscStoreEnvelopeId, WscStoreObstruction, WscStoreObstructionKind,
WscStorePort, WscStoreRecordKind, WscStoreSubject, WscStoreWriteReceipt, WscTopologyRecords,
WSC_CAUSAL_HISTORY_EXPORT_PROFILE_VERSION,
};
pub use validate::validate_wsc;
pub use view::{AttachmentRef, WarpView, WscFile};
Expand Down
Loading
Loading