Guide

Choosing a variant

TNIDv0 and TNIDv1 are the two kinds of TNID. Use TNIDv0 unless the TNID is public and its creation time must stay secret. Then use TNIDv1, or store TNIDv0 and show it encrypted as TNIDv1 with a secret key.

KindPayload (100 bits)LikeUse for
TNIDv0 (time-sortable)Millisecond timestamp, 57 random bitsUUIDv7Database keys, events, anything kept in creation order
TNIDv1 (random)100 random bitsUUIDv4Public TNIDs whose creation time must stay secret, when you don't encrypt TNIDv0
Variants 2, 3Reserved for future versions of the specDon't create them. Libraries still parse them, so TNIDs from newer software still load.

Create TNIDv0 with new_v0, TNIDv1 with new_v1.

Which to use

  • Time-sortable and internal only, such as database keys: TNIDv0.
  • Public, and anyone may learn its creation time: TNIDv0.
  • Public, and its creation time must stay secret: TNIDv1. If you also want time-sortable database keys, store TNIDv0 and show it encrypted as TNIDv1.

TNIDv0 inside, TNIDv1 outside

The optional encryption extension turns a stored TNIDv0 into a TNIDv1 for display, and decrypts it back when a client sends it. The database keeps time-ordered, index-friendly keys, and the public never sees a creation time. The cost is a secret key to manage, and encrypting and decrypting wherever TNIDs enter or leave your app.

TNIDv0: time-sortable

  • Creation order to the millisecond, per name, in every form: string, bytes, u128, and UUID form if all its hex is one case, as for any UUID stored as text.
  • Index-friendly: new rows land at the end of the index; "newest first" is a sort on the TNID.
  • Reveals creation time. As with UUIDv7, ULID and KSUID: anyone who sees a TNIDv0 learns when the record was made, and roughly how many were made around then. To hide creation time: Hiding creation time.
  • Not a replacement for a creation-time column: as with UUIDv7, the time in a TNID is only as accurate as the clock of the machine that made it, and can't be corrected without changing the TNID. Keep a separate timestamp column that records when each row was created.
  • Unordered within a millisecond: the random bits decide.
  • Collisions need the same name and millisecond: a million same-name TNIDs in one millisecond have about a 1 in 290,000 chance that any two collide.

TNIDv1: random

  • Reveals nothing about when or in what order TNIDs were made.
  • Collisions: a billion TNIDv1s with one name have about a 1 in 2.5 trillion chance that any two collide.
  • Unguessable only with a secure random source. The Rust and TypeScript libraries use one; check any other library before relying on unguessable TNIDs, as in a private share link.