feat: follow relay-update announcements with a jittered connect - #18
Merged
Merged
Conversation
Heartwood now sends a kind 24135 relay-update (identical construction to a lock announcement, only the sealed t differs) when its relays change outside a restart. judge already refuses to prompt for it and withRelaysFrom already follows the relays; add the firmware's own vector and tests proving both hold, plus the duplicate/stale cases. Add RelayGate/RelayJitter so a relay Cambium has never spoken to before is not connected the instant a message teaches it about it -- a uniform 30 second to 10 minute jitter, drawn from SecureRandom by default and injectable for tests, so that relay cannot correlate its first connection to the moment the board's broadcast changed. A relay already known from pairing, enrolment or a prior session is unaffected. deliver's relay choice and sync's own return are untouched by the jitter, so answering a genuine lock prompt is never held up by it.
deliver() called sync() and then published to match.context.relays, but sync() now only connects relayGate-ready relays, and RelayWatch.publish falls back to whatever is already connected. If the board had moved to a new relay less than a jitter ago and then restarted locked on that same relay, a tap would send the 24136 to the board's stale relays -- exactly the case relay-update exists to avoid -- until the jitter elapsed (up to 10 minutes). deliver() now trusts a genuine lock prompt's own relays outright before calling sync(): the owner's tap already exposes the timing, so there is nothing left for the jitter to protect there. Guarded on t == "locked" even though judge() already guarantees a relay-update can never reach this method. sync() blocks on RelayWatch.locks's own connection wait, so by the time deliver() reaches publish() the newly trusted relay is normally already connected; publish()'s fallback to whatever succeeded is left as the fastest remaining option for a relay that failed to connect in that window, rather than blocking every delivery on a relay that may not answer at all. Also prune a forgotten board's relays from RelayGate's ready set, unless another remaining board still needs one of them.
Merged
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.
Heartwood firmware (heartwood-esp32 branch
feat/unlock-relay-update, not yet merged) tells enrolled phones when the board's relays change. The update is a kind 24135 built exactly like a lock announcement, with the sealedtset to"relays"instead of"locked".What changes
phone-unlock-v1-relays.json), with tests showing that an update opens, is judgedNOT_LOCKED, never prompts (including when duplicate or stale), and that Cambium still follows its relays.RelayGate/RelayJitter: a relay learned from a message waits a random 30 s to 10 min (SecureRandom) before Cambium connects to it, so a new relay can't tie the phone to the board by timing. A board's relays from pairing, enrolment or app start are trusted at once.deliver()trusts the lock prompt's own relays before syncing, so after a relay change the 24136 reaches the board's new relay rather than the old ones. The owner's tap already exposes that timing.Works with current firmware: 0.5.0 already judged unknown
tvalues asNOT_LOCKED.Tested
./gradlew :app:testDebugUnitTest: 240 tests pass.Not tested
UnlockCoordinatorhas no JVM tests (no Robolectric in this project), so its wiring is covered only at theRelayGatelevel and by reading the code.