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.
| Kind | Payload (100 bits) | Like | Use for |
|---|---|---|---|
| TNIDv0 (time-sortable) | Millisecond timestamp, 57 random bits | UUIDv7 | Database keys, events, anything kept in creation order |
| TNIDv1 (random) | 100 random bits | UUIDv4 | Public TNIDs whose creation time must stay secret, when you don't encrypt TNIDv0 |
| Variants 2, 3 | Reserved for future versions of the spec | Don'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.