DocumentsRunning it

Clock discipline

2 min readSource clock-discipline.mdsha256 7c0a868f436817de…

Where time matters in Mirror#

WhatLimitIf a clock is wrong
Login challenge2 minutesSign-in fails; nothing is exposed
Member session1 hour at mostIt expires early or late by the skew
Webhook freshness5 minutes; 60 s into the futureA fresh delivery is refused and retried
Delegation window30 days at mostA desk key starts or stops early or late
Tree heads, governance eventsStamped with the signer's clockOrdering questions; the log itself is hash-linked, not clock-ordered
Corridor-key rotationBy corridor version, never by clockClock skew cannot split a shipment across keys

The design keeps every safety property off the wall clock. Verdicts do not depend on it; key rotation does not depend on it. What remains are freshness windows, where a wrong clock causes refusals, not false clears.

Budget and monitoring#

  • Budget: keep every Mirror host and member host within 1 second of UTC. The tightest window above (60 s of future skew on a delivery) leaves a wide margin.
  • Member side: discipline the clock with NTP against at least three independent sources (or PTP where you already run it), and alert at 500 ms.
  • Checking the operator from your side, provably. POST /time with a fresh nonce returns the operator's time, signed and bound to that nonce. The operator stamped it somewhere between your send and your receive. So from your own round trip you get an exact interval for the offset: no model of the network, and a replayed old time is refused.
    • mirror-clock.assess(samples, { budget_ms }) takes the tightest sample and answers:
      • within_budget only when the whole interval fits;
      • out_of_budget when the whole interval lies outside;
      • undetermined when the round trip is too long to tell.
    • The SDK's client.clock() runs it.
  • Schedule the check (every 15 minutes is plenty) and alert on anything but within_budget.

What this does not claim#

  • It is a bound against your clock. If your clock is wrong, the bound is against a wrong clock.
  • Nothing is claimed about Cloudflare's time-keeping beyond what /time measures. Inside one Worker request, time advances only on I/O, so a signed time is coarse at that grain.
  • It is not Roughtime: one operator and one signature, not a chain of servers cross-checking each other.