geoprobe-target: bound offset replay with a per-key slot floor - #4308
Draft
nikw9944 wants to merge 5 commits into
Draft
geoprobe-target: bound offset replay with a per-key slot floor#4308nikw9944 wants to merge 5 commits into
nikw9944 wants to merge 5 commits into
Conversation
A valid signature never expires, so an offset captured off the wire could be replayed into location_offsets forever. MeasurementSlot is inside the signed payload, so the highest slot a sender has proven is a lower bound on real time that a replay can repeat but cannot advance: reject offsets more than 2m below that floor, and reject repeats once the floor has stood still for 30m. Derived from the offset stream rather than a ledger clock because geoprobe-target holds no RPC connection by design. Also stop asserting signature_valid=true when -verify-signatures=false means nothing was checked, and stop MinCache.Update resetting best's expiry clock on an equal RTT, matching the agent's own cache. Refs #4286
Review found two ways the floor did not hold. SenderPubkey is signed but unauthenticated, so a minted keypair stamping a real geoprobe's SenderPubkey with a huge slot would have locked that geoprobe out of location_offsets; floors are now keyed by AuthorityPubkey, the key VerifyOffsetChain actually checks against. And a rejected offer did not refresh lastSeen, so a sustained replay outlived its own entry and reseeded from itself every TTL instead of once per restart. Also give the floor its own retention constant rather than borrowing the display cache's -max-offset-age, tie maxFloorStall to geoprobe.SlotCacheTTL, and derive signature_valid from the check result rather than from the flag. Refs #4286
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 of Changes
MeasurementSlotfloor: an offset more than 2 minutes below the highest slot that key has proven is dropped, as are repeats once the floor has stood still for 30 minutes.location_offsets— the table the public geolocation explorer aggregates — forever. geoprobe: reject forged, replayed and spoofed geolocation input #4271 closed the forgery half of RFC-16's mitigation here; this closes the replay half.signature_validno longer asserts a check that did not run, andMinCache.Updateno longer restartsbest's expiry clock on an equal RTT (matching the agent's own cache).The floor comes from the offset stream, not a ledger clock. geoprobe-target holds no RPC connection by design — RFC-16 makes it example target software whose premise is verifying with no chain access — and an absolute check would couple it to the sending probe's RPC health, since #4271 lets a probe stamp a frozen cached slot during its own outage.
MeasurementSlotis signed, so the highest slot a key has proven is a lower bound on real time a replay can repeat but not advance.CLAUDE.mdrecords the invariant.30m reads looser than the agent's 15m lag, but the steady-state number is tighter. A healthy sender's floor advances every ~5 minutes, so the real window is about 7 minutes.
maxFloorStallonly covers a probe riding out its own RPC outage, where a dropped measurement is lost permanently rather than deferred.This does not make the ingest path trustworthy.
VerifyOffsetChainchecks the signature against theAuthorityPubkeyin the same datagram, and this daemon has no allowlist of known probe keys, so any freshly generated keypair can still write attacker-chosen coordinates. Closing that needs the onchain geoprobe registry, which the lake indexer already ingests and this daemon deliberately cannot reach — target enforces freshness, consumer enforces identity. An issue covering both halves is being filed separately;Rejections are logged, not counted. geoprobe-target has no prometheus metrics, so these
Warnlines are the only signal that a sender stopped being ingested. Each carries a stable message plus separatereason(slot_regressed/floor_stalled),authority_pubkey,sender_pubkey,offset_slot,floor_slotandfloor_age_secondsfields. A counter would be a reasonable follow-up.Testing Verification
>rule would drop nine of every ten legitimate offsets.SenderPubkeydoes not lock that geoprobe out; and a sustained replay against a stalled floor stays rejected across repeated sweeps rather than outliving its entry and reseeding from itself.make e2e-test RUN=TestE2E_Geoprobewas not run — it needs Docker and cEOS, unavailable here. Worth a reviewer's time before merge: this changes ingest acceptance, so a false rejection loses a measurement permanently rather than deferring it.changelog.d/nikw9944-adhoc-527.md; the repo has no collation script, so it needs folding intoCHANGELOG.mdby hand.