Skip to content

ch.02 §10: expose the ghost transform normatively — it is the only session-wide wall-clock consensus the protocol produces #8

Description

@1e1f

Summary

Ghost time is the only session-wide wall-clock consensus the protocol produces, but ch. 02 §10 reads as guidance to keep the transform internal. Applications that need a peer-timestamp → local-time mapping therefore re-derive one with a parallel exchange, duplicating work the protocol has already done and done well. Suggest a normative note that implementations SHOULD expose the conversion.

Where

ch. 02 §10 ("Relationship of host, ghost, and client time"):

An implementation needs exactly one conversion, its own G, applied at the protocol boundary; no other peer's host clock is ever observable.

This is exactly right about the wire — no peer's host clock should be observable, and nothing here asks to change that. The issue is that it is the only place the transform's visibility is discussed, and a reasonable implementer reads "applied at the protocol boundary" as "and not surfaced above it."

Why it matters

§4–§5 describe a four-timestamp ping/pong with midpoint estimators, median-filtered over 100+ samples. That is an NTP-shaped offset measurement, and because ghost time is a single session-wide base (§1) rather than a set of pairwise offsets, every member holds a G to the same origin — so one member's ghost timestamp is directly interpretable by another with no additional exchange.

That property is useful well beyond beat scheduling: event timestamping across peers, latency attribution, and control planes layered on a Link session all want precisely it. Without an exposed conversion, each such application runs its own offset protocol alongside Link's, on the same hosts and the same links, to recompute a number Link already has.

Evidence the gap is real, not hypothetical

In link-wire-rs, ghost_offset lives on a private module (crates/tactus/src/lib.rs: mod engine;), and the public Link API surfaces beat-space only — SessionSnapshot carries at_micros / quantum / tempo_bpm / beat / phase / is_playing / enabled / num_peers / session_id, with no time mapping among them. So the reference implementation reflects the reading above. This is an API-surface gap rather than a wire gap: nothing about the protocol needs to change.

Suggested change

A short normative note in §10, after the existing paragraph, along the lines of:

An implementation SHOULD expose its own transform to applications as ghost(t) / host(g). This does not make any peer's host clock observable — it exposes only the local mapping between local host time and the shared session base, which the implementation already computes.

Two things worth stating alongside it, if you take this

Consumers of an exposed transform will need the operating envelope, and it is currently spread across sections:

  1. Slope is fixed at 1 (§2, and the §11 constants table) — only the offset is measured. So the transform corrects offset but never rate, and between re-measurements the error grows at the full crystal-mismatch rate, landing as a step rather than a slew. With re-measurement at 30 s (§7.3) and ±25 ppm per side, that is on the order of 1.5 ms of accumulated error per interval. Any consumer needs that figure and none can currently cite it from one place. (Happy to file this separately if you'd rather keep the issues narrow.)
  2. The RTT and loss envelope in §5.1 already documents that a path at 300 ms RTT fails every measurement attempt, and that re-measurement starvation is silent for an already-joined peer. An exposed transform makes that failure mode application-visible, which is an argument for exposing a staleness indicator (e.g. time since last successful measurement) alongside the conversion rather than the conversion alone.

Filed as a spec consumer; happy to adjust the wording or narrow the scope.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions