Skip to content

Opportunistic clock sync from verified advert timestamps#2423

Closed
pelgraine wants to merge 3 commits intomeshcore-dev:devfrom
pelgraine:clock-sync-from-adverts
Closed

Opportunistic clock sync from verified advert timestamps#2423
pelgraine wants to merge 3 commits intomeshcore-dev:devfrom
pelgraine:clock-sync-from-adverts

Conversation

@pelgraine
Copy link
Copy Markdown

Closes #1329. Related: #2132, #1332, #1882.

Problem

GPS-less repeaters lose their clock on every power cycle, reverting to the firmware default (~May 2024). This means their adverts carry stale timestamps until an admin manually syncs the clock via serial, the mobile app, or a remote management command. For inaccessible repeaters (rooftops, hilltops, solar-powered sites that reboot daily), manual sync is impractical.

Solution

Add an opt-in mechanism for nodes to sync their local RTC from signature-verified advert timestamps. When a node's clock is clearly wrong (before Jan 2025) and it receives a valid advert with a sane timestamp (Jan 2025 – Jan 2036), it adopts that timestamp.

The sync only fires when:

  1. The sub-class opts in via shouldSyncClockFromMesh() (defaults to false)
  2. The local clock is before Jan 2025 (clearly wrong / default state)
  3. The incoming advert timestamp passes sanity checks
  4. The advert signature has already been cryptographically verified
    Once the clock is set past Jan 2025, the sync stops firing — it won't overwrite a valid clock with subsequent adverts.

Changes

src/Mesh.h — New virtual method:

virtual bool shouldSyncClockFromMesh() const { return false; }

src/Mesh.cpp — 14 lines added after advert signature verification (inside the PAYLOAD_TYPE_ADVERT handler), before onAdvertRecv() call. Checks the opt-in gate, validates both local and incoming timestamps, and calls setCurrentTime() if appropriate.

examples/simple_repeater/MyMesh.h — Single-line override:

bool shouldSyncClockFromMesh() const override { return true; }

What this does NOT do

  • Does not affect companion nodes, clients, or room servers (they don't override the method)
  • Does not overwrite a valid clock — only fires when local time is clearly wrong
  • Does not sync backward — a node with a stale clock can't drag others back
  • Does not add any new preferences, CLI commands, or protocol changes
  • Does not increase packet size or airtime

Testing

  1. Flash a repeater without GPS/RTC
  2. Confirm clock shows default (~May 2024) on boot
  3. Wait for a valid advert from any peer with correct time
  4. Confirm serial output shows clock synced from advert: <epoch>
  5. Confirm subsequent adverts do NOT trigger further syncs
  6. Confirm a repeater WITH GPS/RTC is unaffected (clock already valid)

Tested

  • Station G2 repeater (both ESPNow bridge repeater and standard) - example screenshots from test:
IMG_2594 IMG_2595 IMG_2596
  • Heltec v3 espnow bridge repeater
  • T-Echo Lite repeater (no gps) - example screenshots from test:
IMG_2597 IMG_2598 IMG_2599

GPS-less repeaters that lose their clock on power cycle can now
automatically sync from signature-verified advert timestamps.
Opt-in via shouldSyncClockFromMesh() override (default: false).
Only fires when local clock is clearly wrong (before Jan 2025).

Closes meshcore-dev#1329
@4np
Copy link
Copy Markdown

4np commented Apr 28, 2026

I took a stab at peer-based time synchronization in #1443 , but there were genuine issues with the approach of relying on peer-based timed (such as clock poisoning, future dated clocks on ESP32, etcetera). There is also no way of knowing if some other's advert is on the right time.

My PR used a statistical approach (Median Absolute Deviation by sampling many packets, while estimated taking time of flight into account. There are actually quite some genuine concerns by relying on peers to synchronize time, as also voice by reviewers. Also, with the continuous move towards reducing the number of flood adverts the reliance on flood packets for time keeping has also become less reliable.As such I closed the PR.

This PR doesn't have any safeguards, doesn't use statistics, doesn't take time of flight into account and has the same issues (and more) trusting peer time. This PR simply takes any packet that comes around that has a time between Jan 2025 and Jan 2026 and updated their time to match the packet. If I were to send a flood advert using a timestamp set to April 1st, 2030, your repeater would happily adopt it.

Comment thread src/Mesh.cpp
if (shouldSyncClockFromMesh()) {
uint32_t local_time = _rtc->getCurrentTime();
if (local_time < 1735689600UL // local clock before Jan 2025 (clearly wrong)
&& timestamp > 1735689600UL // advert after Jan 2025
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You cannot assume a packet with a time between Jan 2025 and Jan 2036 has the right time, it can be off by years.

@pelgraine pelgraine closed this Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants