-
Notifications
You must be signed in to change notification settings - Fork 0
feat(devices): self-healing WCK rotation after revoke (#134) #137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package cli | ||
|
|
||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "time" | ||
|
|
||
| "github.com/Reederey87/DevStrap/internal/state" | ||
| ) | ||
|
|
||
| // wckRotationPendingMetaKey is the local_meta row recording that a WCK | ||
| // rotation is OWED: a `devices revoke`/`lost` could not rotate the epoch, so | ||
| // events keep sealing under a key the revoked device still holds until a | ||
| // rotation lands (issue #134). Machine-local retry bookkeeping only — it never | ||
| // rides the event log. | ||
| const wckRotationPendingMetaKey = "wck_rotation_pending" | ||
|
|
||
| // wckRotationPendingRecord is the JSON value of the pending row. Epoch records | ||
| // the epoch that was ACTIVE when the rotation failed (diagnostic only — see | ||
| // wckRotationPendingSince for why it must NOT drive resolution). | ||
| type wckRotationPendingRecord struct { | ||
| Epoch int64 `json:"epoch"` | ||
| Since time.Time `json:"since"` | ||
| } | ||
|
|
||
| // markWCKRotationPending persists the owed-rotation marker after a failed | ||
| // revoke-path rotation so sync's rotation gate retries it on every cycle. | ||
| func markWCKRotationPending(ctx context.Context, store *state.Store, epoch int64) error { | ||
| raw, err := json.Marshal(wckRotationPendingRecord{Epoch: epoch, Since: time.Now().UTC()}) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| return store.SetLocalMeta(ctx, wckRotationPendingMetaKey, string(raw)) | ||
| } | ||
|
|
||
| // wckRotationPendingSince reports whether a rotation is still owed. The marker | ||
| // resolves ONLY via clearWCKRotationPending after THIS device's own successful | ||
| // Rotate — never by observing a newer epoch (adversarial-review HIGH, issue | ||
| // #134): a peer that has not yet pulled the revoke can rotate for age reasons | ||
| // and grant the new epoch to the still-approved-in-its-registry revoked | ||
| // device, so "any newer epoch is active" is NOT proof the revoked device was | ||
| // excluded. A locally-run Rotate always excludes locally-revoked devices, | ||
| // which is exactly the proof the marker needs; the worst case of ignoring a | ||
| // legitimate peer rotation is one redundant epoch. A marker that fails to | ||
| // parse stays pending with a zero Since (fail-closed). | ||
| func wckRotationPendingSince(ctx context.Context, store *state.Store) (time.Time, bool, error) { | ||
| raw, ok, err := store.GetLocalMeta(ctx, wckRotationPendingMetaKey) | ||
| if err != nil || !ok { | ||
| return time.Time{}, false, err | ||
| } | ||
| var rec wckRotationPendingRecord | ||
| if jerr := json.Unmarshal([]byte(raw), &rec); jerr != nil { | ||
| return time.Time{}, true, nil | ||
| } | ||
| return rec.Since, true, nil | ||
| } | ||
|
|
||
| // clearWCKRotationPending removes the marker. Called ONLY after a successful | ||
| // local Rotate (sync's owed retry, `keys rotate`, or a later revoke whose | ||
| // rotation succeeded) — every local Rotate wraps to ApprovedRecipients, which | ||
| // excludes all locally-revoked devices, satisfying the owed containment. | ||
| func clearWCKRotationPending(ctx context.Context, store *state.Store) error { | ||
| return store.DeleteLocalMeta(ctx, wckRotationPendingMetaKey) | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Misleading error and dropped read error when
CurrentKeyEpochfails.When
aerr != nil,afteris its zero value, so the message reads e.g. "epoch advanced 5→0; grants may not have published" — a mid-commit claim that didn't actually happen — and the real DB read error (aerr) is discarded (onlyrerris wrapped). Failing closed here is correct, but the two cases should be reported distinctly so an operator debugging a revoke-containment failure isn't sent down the wrong path.🩹 Distinguish the read-failure case
📝 Committable suggestion
🤖 Prompt for AI Agents