fix(koinon): close three tamper-log integrity gaps in the writer path - #391
Merged
Conversation
Fixes three defects in the append-only hash-chained tamper log that each let an ordinary failure mode be mistaken for, or actually become, undetectable tampering: - Seal-refresh failure after a durable append (#285): TamperLog::open classified a log strictly AHEAD of its own valid seal identically to genuine trailing truncation and refused to resume it. New ChainStatus::Unsealed distinguishes the two by direction: only a chain-key holder can produce entries that verify past a valid seal's count, so that direction is safe to resume and re-seal, while a seal claiming MORE entries than verified (truncation) stays refused exactly as before. - No single-writer enforcement (#226): TamperLog::open now acquires an exclusive advisory lock on a per-log lock sidecar, held for the writer's lifetime and released automatically on process death. kryphos::Vault additionally serializes the open-through-append critical section behind an in-process mutex, since koinon's lock is fail-fast (non-blocking) and same-process threads need to queue, not race. - Rotation reset the chain to genesis (#211): TamperLog::rotate no longer resets prev_hash; the new segment is seeded from the outgoing segment's terminal hash, persisted in the seal as segment_start_hash (now part of the seal's MAC). New verify_segment_chain walks a rotated log's full segment set re-deriving each link, so a deleted segment - including the one immediately before the live file, which leaves no numeric gap - fails to verify rather than being silently invisible. tamper_log.rs split into tamper_log_lock.rs, tamper_log_verify.rs, and tamper_log_segments.rs to stay under the 800-line file threshold. Closes #285 Closes #226 Closes #211
added 2 commits
August 18, 2026 13:22
# Conflicts: # crates/kryphos/src/storage.rs
Refs #285 The first full-gate run on the merge commit failed compiling the lib-test target: a_seal_destroyed_by_a_failed_rename_stays_fail_closed formatted a Result<TamperLog, _> with {:?}, but TamperLog deliberately has no Debug impl (it holds the ChainKey; koinon follows RUST/no-debug-derive-on-public-types). Format the assertion outcome without naming the Ok payload. Latent on the branch — the conflicting PR never had a merge ref, so the test target was never compiled in CI. Also restores the fs2 dependency edge under koinon in Cargo.lock, which the three-way lockfile merge dropped while koinon's manifest requires fs2.workspace.
added 3 commits
August 19, 2026 07:08
…ving segment chain Refs #285 verify_segment_chain seeds each segment's stream from the terminal hash the previous segment actually produced, but a live file with no entries of its own has no first link for that seed to be checked against — so deleting the most recent rotated segment left a contiguous numbering and an Intact verdict, exactly the gap the module doc claims is caught. Require a valid live seal to name the terminal hash the previous SURVIVING segment actually produced (keyed genesis when unrotated), reporting Broken with both hashes on mismatch. #285's Unsealed leniency is unaffected: a stale-but-valid seal carries the file's fixed segment_start_hash.
…e attempted Refs #285 concurrent_opens_on_the_same_path_never_both_succeed evaluated open(...).is_ok() inside the racing thread, dropping a winning TamperLog (and releasing its flock) before the loser was necessarily scheduled — under load skew both opens could succeed. Return the Result from the thread and join both before counting, so the winner's lock stays held until both attempts have completed; exactly-one-winner is now deterministic rather than probabilistic.
forkwright
pushed a commit
that referenced
this pull request
Aug 19, 2026
🤖 I have created a release *beep* *boop* --- ## [0.2.0](v0.1.25...v0.2.0) (2026-08-19) ### Features * **kerykeion:** author the wire schema from the protocol, dropping the vendored GPL protos ([#392](#392)) ([aa81813](aa81813)) ### Bug Fixes * **koinon:** close three tamper-log integrity gaps in the writer path ([#391](#391)) ([c947eee](c947eee)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.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.
Refs #285.
Absorbed from the down akroasis seat's unlanded branch (fresh 2026-08-17, zero PR). One commit: fix(koinon) closing three tamper-log integrity gaps in the writer path (~+1359/-252 across 10 files, including ~442 lines of recovery tests). Seal/verify/segment handling and a lock split per the branch measurement.
Security-relevant (tamper-log recovery) — landing should include a fresh review pass, not just green CI.