cubesys: durability fixes A/B/C verified live on VM

- Fix A (store.rs): checkpoint boundary persists wal.committed_seq (highest
  fsync'd) instead of wal.seq() (next-to-assign), which skipped all WAL
  entries since last checkpoint -> silent data loss on reboot.
- Fix B (commands.rs open): run decrypted program in isolated read_snapshot()
  clone instead of put_raw plaintext over sealed envelope (stopped reboot-time
  EnvelopeTooShort / clobber).
- Fix C (commands.rs keyinit): flush OS key cells to WAL via log_put so they
  fold into base snapshot and survive reboot (keyinit #2 issues 0, not 2);
  previously re-minted random material each boot -> sealed records unopenable.
- Regression guards durable_sealed_record_survives_restart +
  open_does_not_clobber_sealed_record in cubesys/src/commands.rs.
- STARTUP-README: replace stale 'EPHEMERAL across restarts' caveat with the
  fixed/verified durability note.
Verified live: systemctl restart cube-server (VM reboot path) -> sealed record
decrypts+executes after reboot; key cell byte-identical; keyinit idempotent.
This commit is contained in:
CUBELinux-2
2026-08-13 12:31:34 -04:00
parent ab40409316
commit f6bd8bd01f
6 changed files with 453 additions and 33 deletions
+17 -7
View File
@@ -801,9 +801,16 @@ fn checkpoint_store(
));
}
if f.write_all(buf.as_bytes()).is_ok() && f.flush().is_ok() && f.sync_all().is_ok() {
persist_seq(cp_seq_path, wal.seq.load(Ordering::SeqCst));
wal.set_cp_seq_wal(wal.seq.load(Ordering::SeqCst));
wal.set_base_seq(wal.seq.load(Ordering::SeqCst));
// Boundary must be the highest *durable* (fsync'd) WAL seq,
// NOT `wal.seq()` (which is the next-to-assign counter and
// sits one past the last entry). Persisting the next-to-
// assign value made `replay_after` skip every still-valid
// WAL entry on restart — i.e. silent data loss of any
// record written since the previous checkpoint.
let durable = wal.committed_seq.load(Ordering::SeqCst);
persist_seq(cp_seq_path, durable);
wal.set_cp_seq_wal(durable);
wal.set_base_seq(durable);
}
}
}
@@ -857,10 +864,13 @@ fn fold_delta_into_base(
}
// Delta is now fully represented by the base; truncate it.
let _ = fs::write(delta_path, b"");
let seq = wal.seq.load(Ordering::SeqCst);
persist_seq(cp_seq_path, seq);
wal.set_cp_seq_wal(seq);
wal.set_base_seq(seq);
// Boundary = highest *durable* WAL seq (fsync'd), not `wal.seq()` (the
// next-to-assign counter, which sits one past the last entry and would
// make `replay_after` skip still-valid entries on restart).
let durable = wal.committed_seq.load(Ordering::SeqCst);
persist_seq(cp_seq_path, durable);
wal.set_cp_seq_wal(durable);
wal.set_base_seq(durable);
}
/// Read `db_path` (full base) then apply the delta file; returns the