OverviewFilesCommitsIssuesDocsReleasesPeers

atproto-store commits / 7792fe6

tid: nextRev — deterministic rev continuation (pure function of the prev rev)
The atproto chain can only be indexer-agnostic if `rev` is derived from the log rather than the wall clock: two co-owners continuing from the SAME head over the SAME finalized record set must compute byte-identical commits (RFC6979 signing is already deterministic), so concurrent signers are idempotent instead of forking. Mirrors atproto-base/lib/rev.js byte-for-byte (clockId 0) — verified identical output — so the two implementations agree exactly and can converge here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7792fe6f7f7d4340843682b41ae359b5f66a09b0
by plankatron <fleeky@gmail.com> · 2026-07-24T20:17:41.000Z

Changed files (1) +16 -0

lib/tid.js +16 -0
……@@ -24,3 +24,19 @@ export function tidNow (clockId = 0, clock = () => BigInt(Date.now()) * 1000n) {
2424export function isValidTid (s) {
2525 return typeof s === 'string' && s.length === 13 && [...s].every(c => S32.includes(c))
2626}
27
28// The micros component of a TID (drops the 10-bit clock id).
29export function tidMicros (tid) {
30 let n = 0n
31 for (const ch of String(tid)) n = (n << 5n) | BigInt(S32.indexOf(ch))
32 return n >> 10n
33}
34
35// The DETERMINISTIC next rev after `prevRev` — a pure function of the previous rev, never the
36// wall clock. This is what makes the atproto chain indexer-agnostic: two co-owners continuing
37// from the SAME head over the SAME finalized record set produce byte-identical commits (RFC6979
38// signing is deterministic), so concurrent signers are idempotent rather than forking. Mirrors
39// atproto-base/lib/rev.js byte-for-byte (clockId 0) so both implementations agree exactly.
40export function nextRev (prevRev) {
41 return s32encode((tidMicros(prevRev) + 1n) << 10n)
42}