API reference@evolu/commonlocal‑first/Timestamp › Timestamp

Defined in: packages/common/src/local-first/Timestamp.ts:442

Hybrid Logical Clock timestamp.

Timestamps serve as globally unique, causally ordered identifiers for CRDT messages in Evolu's sync protocol.

Why Hybrid Logical Clocks

Evolu uses Hybrid Logical Clocks (HLC), which combine physical time (millis) with a logical counter. This hybrid approach preserves causality like logical clocks while staying close to physical time for better human interpretability.

The counter component ensures causality is maintained even when physical clocks are imperfect. When clocks drift or operations occur concurrently, the counter increments to establish a total order. This means Evolu achieves well-defined, eventually-consistent behavior regardless of physical clock accuracy.

When the 16-bit counter is exhausted, the logical millisecond advances by one and the counter resets to zero. The resulting timestamp must still fit within Millis. If rollover exceeds TimestampConfig.maxDrift, sendTimestamp and receiveTimestamp return TimestampDriftError with the candidate timestamp for the database to handle. Rollover preserves deterministic ordering even when a batch uses one captured wall time.

Vector clocks can accurately track causality and detect concurrent operations, but they require unbounded space in peer-to-peer systems and crucially, still don't solve our fundamental problem: when they detect operations as concurrent, we still need a deterministic way to choose a winner. Additionally, any deterministic conflict resolution can be gamed by malicious actors.

HLC timestamps work well in practice because modern device clocks accurately reflect the order of sequential edits in the common case. The database uses the drift limit to quarantine messages whose own timestamps are too far ahead of its system time without applying them to application tables. Quarantined messages remain stored and synchronized; each receiving device checks drift against its own system time.

References

Privacy Considerations

Timestamps are metadata visible to relays and collaborators. While it can be considered a privacy leak, let us explain why it's necessary, and how to avoid it if maximum privacy is required.

With real-time communication, participants always see activity (receiving bytes). We cannot trust anyone not to store that information, so explicitly exposing timestamps doesn't add additional risk.

If we really want not to leak user activity, we can implement a local write queue:

  1. Write changes immediately to a local-only table
  2. Periodically and randomly flush messages to sync tables

Trade-off: It breaks real-time collaboration.

Another technique is generating fake random activity (dummy messages) to mask real usage patterns. This preserves real-time collaboration but increases storage and bandwidth usage.

Extends

Properties

counter

counter: number &
  Brand<"NonNaN"> &
  Brand<"Finite"> &
  Brand<"Int"> &
  Brand<"NonNegative"> &
  Brand<"LessThanOrEqualTo65535"> &
  Brand<"Counter">;

Inherited from

InferType.counter;

millis

millis: number &
  Brand<"NonNaN"> &
  Brand<"Finite"> &
  Brand<"Int"> &
  Brand<"NonNegative"> &
  Brand<"LessThan281474976710655"> &
  Brand<"Millis">;

Inherited from

InferType.millis;

nodeId

nodeId: string & Brand<"NodeId">;

Inherited from

InferType.nodeId;