uuid.lol

UUID v8 Generator

Press c to copy or r for a new one

Generate in bulk

v8 is a blank cheque

RFC 9562 fixes exactly six bits of a v8 UUID: the four version bits and the two variant bits. The other 122 are yours. The standard calls it “experimental or vendor-specific” and declines to say anything else about the layout.

So there is no single correct v8. The one above fills all 122 free bits with randomness, which makes it a v4 wearing a different version number. That is a demonstration, not a recommendation.

What people actually put in one

A shard or tenant number in the leading bits, so that IDs from one customer sit together in an index. A timestamp at a resolution the other versions do not offer, microseconds or nanoseconds instead of milliseconds. A hash under a function that is neither MD5 nor SHA-1, which is the only way to get a name-based UUID that is not v3 or v5.

The common thread is that v8 is for encoding something you already have. If you do not have anything to encode, you do not want a v8.

The catch

Nothing that receives your v8 can interpret it. A decoder can tell you the version is 8 and the variant is RFC 9562, and then it stops, because there is nothing else it is allowed to assume. Whatever meaning you pack in travels only as far as your own code.

Put differently: a v8 is a private format that happens to fit in a UUID column. That is a real and useful thing to want. It is also a good reason to check that v7 genuinely will not do first.

Generate one in code

TypeScript

// 122 bits of your own, with the version and variant fixed.
function v8(bytes: Uint8Array) {
  bytes[6] = (bytes[6] & 0x0f) | 0x80;
  bytes[8] = (bytes[8] & 0x3f) | 0x80;
  return [...bytes]
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("")
    .replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, "$1-$2-$3-$4-$5");
}

Python

import os, uuid

def v8(data: bytes = None) -> uuid.UUID:
    return uuid.UUID(bytes=(data or os.urandom(16)), version=8)

Go

import "github.com/google/uuid"

// NewV8 fixes the version and variant bits and leaves the rest to you.
id, err := uuid.NewV8(data)

What is a UUID?

A UUID is a Universally Unique IDentifier: a 128-bit number used to identify something without coordinating with anyone else. It is usually written as 32 hexadecimal characters in five hyphenated groups.

UUIDs come in several versions. v4 is random, v1 and v6 encode a timestamp and a MAC address, v3 and v5 hash a name inside a namespace, and v7 encodes a Unix millisecond timestamp followed by random bits.

Why use UUIDs?

UUIDs can be generated anywhere, by anyone, with no central authority, and still be safely assumed not to collide. That makes them useful for distributed systems, offline clients, and merging data from separate databases.

They are also instantly recognisable, which makes them easy to pick out of logs.

Some databases (for example Postgres) store a UUID as a 128-bit integer rather than a 36-character string, so using one as a primary key costs far less than it looks.

What are the downsides?

Uniqueness is probabilistic, not guaranteed. The odds of two v4 UUIDs colliding are negligible, but they are not zero.

Some versions leak information. A v1 or v6 UUID contains the MAC address of the machine that generated it and the moment it was generated, which is enough to correlate a device across systems. You can see exactly what a UUID gives away with the UUID decoder.

Time-ordered IDs are also partly guessable, so they are a poor choice for anything that doubles as a secret, such as a password reset token.