Repository navigation
Keep a pairing's unconfirmed long-term PSK out of the record saves behind it - #91
Merged
Merged
Conversation
…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.
Contributor
There was a problem hiding this comment.
🟢 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.
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.
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 throughFailedWrites) 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"; ifput_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, writeswithout_change(): the record asput_back()leaves it (the earlier long-term PSK and, unlessset_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.save_change(): when it lands but a save was made meanwhile, it saves the record again, so the change is durable (andserver/pair-finalizesent) only once a save of it lands with none made meanwhile. A refused re-save puts the change back.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.save_record(client_id)overload lost itsdoneparameter, 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.HeldProvidernow scripts outcomes (it derives fromScriptedProvider);HoldsOneLongTermPskChangePerClient's comment counts the re-save.Closes #56