Skip to content

fix(koinon): close three tamper-log integrity gaps in the writer path - #391

Merged
forkwright merged 6 commits into
mainfrom
fix/285-tamper-log-integrity
Aug 19, 2026
Merged

fix(koinon): close three tamper-log integrity gaps in the writer path#391
forkwright merged 6 commits into
mainfrom
fix/285-tamper-log-integrity

Conversation

@forkwright

Copy link
Copy Markdown
Owner

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.

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
@forkwright forkwright closed this Aug 18, 2026
@forkwright forkwright reopened this Aug 18, 2026
forkwright 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.
@forkwright forkwright closed this Aug 19, 2026
@forkwright forkwright reopened this Aug 19, 2026
forkwright 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
forkwright merged commit c947eee into main Aug 19, 2026
8 checks passed
@forkwright
forkwright deleted the fix/285-tamper-log-integrity branch August 19, 2026 12:55
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant