Skip to content

Keep a pairing's unconfirmed long-term PSK out of the record saves behind it - #91

Merged
kahrendt merged 1 commit into
mainfrom
issue-56-unconfirmed-psk
Oct 15, 2026
Merged

kahrendt merged 1 commit into
mainfrom
issue-56-unconfirmed-psk

Conversation

@kahrendt

Copy link
Copy Markdown
Contributor

Summary

While a pairing's long-term PSK save was in flight, a name or approval save of the same record (refresh_record(), set_approval(), or such a save retried through FailedWrites) encoded the new PSK too. If the PSK save failed and that other save landed, the PSK reached disk unconfirmed, against pairing.md "server/pair-finalize"; if put_back()'s write then failed as well, the next start referenced a PSK the client never received.

Change

  • save_record(client_id), while the client's PSK change is pending, writes without_change(): the record as put_back() leaves it (the earlier long-term PSK and, unless set_approval() ran meanwhile, the earlier approval, with the other fields from memory), or nothing for a record the change created. put_back() now uses the same function.
  • The change's own save goes through save_change(): when it lands but a save was made meanwhile, it saves the record again, so the change is durable (and server/pair-finalize sent) only once a save of it lands with none made meanwhile. A refused re-save puts the change back.
  • This differs from the issue's Direction, which saved once more after success. That extra save came after done(true), so a refused extra save could leave a finalized PSK off disk, and the client would have to pair again. Waiting for the re-save costs the same one extra write, only when a save was made meanwhile.
  • The save_record(client_id) overload lost its done parameter, which no caller passed. A second overload encodes a given record and carries the save contract.
  • docs/conventions.md "Persisted before use" states the rule.

Known trade-off: if the first PSK save lands, the meanwhile save and the re-save are refused, and put_back()'s write and its retry at stop are refused too, disk keeps that first save's PSK. That takes as many refusals as the issue's scenario.

Tests

  • ServerCoreTrust.KeepsAnUnconfirmedLongTermPskOffDisk: a held, scripted provider and a name save, an approval save or both made while the PSK save is pending. Rows: the issue's scenario; a record the change created; a refused re-save; a meanwhile save refused and retried ahead of another slot's write; and two Control rows where the PSK save lands. Each row checks the change's outcome and what the next start loads. The failure rows fail on main.
  • HeldProvider now scripts outcomes (it derives from ScriptedProvider); HoldsOneLongTermPskChangePerClient's comment counts the re-save.

Closes #56

…hind it

While a pairing's long-term PSK save was in flight, a name or approval
save of the same record encoded the new PSK too. If the PSK save failed
and that save landed, the PSK reached disk unconfirmed, and only the
put-back's write removed it again.

A save made while the PSK save is pending now writes the record as
put_back() would leave it (without_change(), which put_back() now uses
too), or nothing for a record the change created. The change's own save
(save_change()) runs again when such a save was made meanwhile, so the
change is durable, and server/pair-finalize sent, only once a save of it
lands with none made meanwhile.
@kahrendt
kahrendt marked this pull request as ready for review October 15, 2026 19:01
Copilot AI balanced review requested due to automatic review settings October 15, 2026 19:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No blocking issues were identified in save ordering, rollback, or regression coverage; tests were not run.

0 open findings

What changed in this PR

Prevents unrelated client-record saves from persisting an unconfirmed pairing PSK.

Changes:

  • Excludes pending PSK changes from intervening record saves.
  • Waits for an uncontested successful save before confirming durability.
  • Adds scripted regression cases and documents the persistence rule.
File Description
tests/​test_server_trust.cpp Adds failure and success cases for overlapping saves.
src/​server_core.h Declares pending-save tracking and persistence helpers.
src/​server_core.cpp Implements filtered saves, re-saves, and shared rollback logic.
docs/​conventions.md Clarifies credential persistence requirements.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@kahrendt
kahrendt enabled auto-merge (squash) October 15, 2026 19:09
@kahrendt
kahrendt merged commit 9331eb7 into main Oct 15, 2026
14 checks passed
@kahrendt
kahrendt deleted the issue-56-unconfirmed-psk branch October 15, 2026 19:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Keep a pairing's unconfirmed long-term PSK out of the record saves made while it is being saved

2 participants