How it works
- Record: every minute the clock-discipline software's current measurements are written as a one-line JSON record: state, stratum, offset, error bound, source counts and any events.
- Tree: the records of one UTC day are the leaves of a Merkle tree. The tree root is a single 32-byte digest of all of that day's records; change one character in one record and the root changes.
- Signature: with every new record the day's head — the day, the record count, the root and the previous day's root — is signed with the log key.
- Chain: because each day carries the previous day's root, the days are linked; altering a past day breaks the signatures of every day after it.
- Verification: the query above downloads the day's records, recomputes the root in your browser and checks the signature. You do not need to trust the server; knowing the public key is enough.
Log public key (Ed25519, base64) — click to copy:
—Record fields
| Field | Meaning |
|---|---|
| t | The moment the record was written (UTC, ISO 8601) |
| seq | The record's sequence number within the day; its leaf index in the tree |
| state | locked locked to sources · holdover no source, running on its own oscillator · unsynchronised not synchronised |
| stratum | Stratum at that moment |
| reference | The source steering the clock: country / type / protection / position |
| offset | Instantaneous offset of the system clock from the selected source (seconds) |
| rms_offset | Long-term average of the offset (seconds) |
| root_delay · root_dispersion | Round trip to the primary clock and accumulated uncertainty (seconds) |
| error_bound | Published error bound: root delay ÷ 2 + root dispersion (seconds) |
| frequency_ppm · skew_ppm | Oscillator correction and its uncertainty |
| leap | Leap second status |
| sources | Total, usable and NTS-protected source counts |
| events | logger_start, state_change, reference_change, stratum_change |
Files
| /timelog/index.json | First and last day of the log, public key |
| /timelog/latest.json | Latest record and latest signed head |
| /timelog/YYYY/MM/DD.jsonl | That day's records, one per line |
| /timelog/YYYY/MM/DD.head.json | That day's signed head |
| /timelog/pubkey.pem · pubkey.txt | Log public key |
| /timelog/verify.py | Proof bundle verifier |
The signed body of a head has five lines: log name, day, record count, root (base64) and the previous day's root. A leaf hash is SHA-256(0x00 ‖ line) and a node hash is SHA-256(0x01 ‖ left ‖ right) (RFC 6962).
Verifying from the command line
The proof bundle you download is self-contained: it holds the record, the path linking the record to the root, the signed head and the public key. The verifier does not contact any server.
curl -O https://ntp.nerzen.com/timelog/verify.py python3 verify.py nerzen-timeproof-*.json \ --key "$(curl -s https://ntp.nerzen.com/timelog/pubkey.txt)"
curl -s https://ntp.nerzen.com/timelog/latest.json # latest record + signed head curl -s https://ntp.nerzen.com/timelog/2026/10/01.jsonl | wc -l # must equal the record count in the head
Limits
- It is self-attested. The log records what the server itself measured; it proves what we reported at that moment and that we did not change it later, not that the clock was truly correct.
- The signing key is on the server. Someone who compromises the server could sign new records. They cannot, however, alter a past day unnoticed while anyone holds a previously saved head for that day.
- No independent witness. For now heads are signed only with our key. To strengthen the guarantee, save
latest.jsonyourself at regular intervals; we cannot present a history that contradicts a head you hold. - Resolution is one minute. A brief event between two records may not appear in the log.
- Not a calibration certificate or a legal timestamp.
- If logging stops, those minutes have no record; the gap is plainly visible from the record times and cannot be filled in later.
Frequently asked questions
What is this log for?
When you later investigate an incident in which your systems took time from us, you can see our server's state at that moment — was it locked, what was the error bound, did the source change — based on a record signed at the time rather than on our word.
What is a proof bundle?
A small JSON file containing one record, the hash path that links it to the day's root, the signed head and the public key. You can attach it to an incident report and verify it offline even years later.
Does it also prove the time I received over NTS?
No. The log records the server's state, not the individual replies given to clients. For a signed individual reply use Roughtime.
Why known structures instead of your own format?
The Merkle tree and hashing rules are the same as those used in certificate transparency logs (RFC 6962); the signature is Ed25519. Verification therefore needs tools everyone already has, not our software.