linkex.cclinkex.cc
The Open Tally Protocol

Time anchoring

Fixing commitments in time — RFC 3161 timestamps and on-chain anchoring on Base, what each provides, the exact anchor construction, and how to walk an anchor down to the chain.

Hash chains prove internal consistency. To prevent the operator from rewriting history wholesale (re-signing a whole alternate ledger), commitment hashes must be fixed in time by parties the operator does not control. Open Tally uses two independent mechanisms, and they deliberately fix the same value — a period commitment's hash, which covers the commitment's full canonical form (first_seq/last_seq/count/root/prev_commitment_hash) and, through the Merkle root, the run of signed receipts beneath it.

RFC 3161 timestamps (TSA)

Each period commitment's hash is submitted to an RFC 3161 Timestamp Authority — in the linkex.ai instance, DigiCert; the returned token is verified and stored, and served with the public ledger (include_tokens=1 on /api/receipt/commitments). The token fixes the commitment hash — and therefore the entire period's content — at the token's time.

A timestamp token rests on one authority: whoever holds the TSA's signing key could in principle issue a token bearing any time. That is why verifiers pin the TSA trust roots (-tsa-roots) rather than deferring to whatever the host operating system trusts — the verification guide shows why this is a requirement, not an optimization — and why a second, unrelated fixing mechanism exists.

On-chain anchoring (Base) — live

The second fixing runs on Base mainnet (eip155:8453), hourly, and is live in the linkex.ai instance: every window since anchoring began has a confirmed public transaction (live stats).

The construction, exactly as published in the anchoring specification (served verbatim at /api/anchor/spec so its hash can be checked without a source checkout):

  1. Window. Each anchor covers the commitments written during one hourly window [window_start_ms, window_end_ms) — selection by write time, not by the period covered, so a commitment written late (say, after a stalled stream clears) is still anchored in the window of its writing rather than never.
  2. Entry projection — data minimization. For each commitment, exactly two values enter the anchored form: a salted pseudonymous anchor_id and the commitment's hash, verbatim as the public ledger serves it. No stream name, no sequence numbers, no counts, and no business data ever goes on-chain.
  3. Fold to a root. Entries, sorted by anchor_id, fold into a hash chain seeded by a domain separator; the result is the window's data_root. An idle hour is anchored as explicitly empty rather than skipped — silence and absence are made distinguishable.
  4. Payload. Five fixed-form fields — window start, window end, data_root, spec_hash, prev_anchor_hash — hash to a single 32-byte payload_hash, which is written on-chain as the calldata of a zero-value self-send from the anchor wallet.

Three of those fields deserve a second look:

  • anchor_id is a salted hash of the stream identity and the commitment's first sequence number. The salt is private and never rotated — a one-way door: outsiders cannot enumerate the operator's channel structure from on-chain data, while the salt holder can still prove ownership of any anchor. A verifier never needs to compute it; it contributes bytes to the fold and a stable pseudonymous handle, not a claim to check.
  • spec_hash is the SHA-256 of the anchoring specification document itself. The on-chain transaction commits to the rules it was made under — fetch the spec, hash it, compare. Rule changes ship as a new spec version with a new hash; historical anchors keep pointing at theirs.
  • prev_anchor_hash chains each window to the previous one's payload_hash, so no window can be silently removed from the anchor ledger.

Broadcasting is two-phase and crash-safe: the signed transaction is persisted before sending, so a crash cannot cause double-signing, and a confirmation scanner converges every anchor to confirmed or failed.

Walk it yourself: five steps, no tooling

The anchor transparency page lays this out per window in the browser; the machine-readable form is /api/anchor/transparency (add include_entries=1 for the entry lists — they are omitted by default because a busy window's list is large). For any window:

  1. Inclusion — find your commitment's hash (from /api/receipt/commitments) among the window's disclosed entries;
  2. Root — fold the entries in their disclosed order per the spec and compare with the record's data_root;
  3. Payload — rebuild the five-field payload from the record and compare its SHA-256 with payload_hash;
  4. Chain — open the record's tx_hash on any public Base explorer: the transaction's 32-byte calldata must equal payload_hash, and the block timestamp is the fixing time;
  5. Continuity — each record's prev_anchor_hash equals the previous record's payload_hash.

None of these steps involves linkex tooling or trust in linkex — steps 4 and 5 are checked against the public chain itself.

Anchoring has a start moment

The anchor ledger begins at the earliest_window_start_ms reported by /api/anchor/stats. Commitments issued before that moment carry RFC 3161 timestamps and sit in the signed commitment chain, but are not inside any on-chain window — anchoring cannot reach back before it was switched on. This is a boundary, not a defect, and it is stated so nobody reads more into an anchor than it fixes.

Why two mechanisms?

RFC 3161 TSAOn-chain anchor
Trust assumptionone timestamp authoritythe public chain's consensus
Failure modeTSA key compromise / cert expirychain reorg (bounded, then final)
Verificationoffline, with pinned rootspublic: anyone can check the transaction

They fix the same hash through unrelated trust paths, so once both are in place they corroborate each other. An operator wanting to rewrite history would need to defeat both — after the fact, for timestamps already in counterparties' hands and transactions already final on a public chain.

On this page