RFC 9562

Universally Unique IDentifiers (UUIDs)

๐Ÿ“… published May 2024, from ietf

Universally Unique IDentifiers (UUIDs) ๐Ÿฆ‹

Okay bestie, let's get the paperwork out of the way first ๐Ÿ“‹โœจ This is RFC 9562, a Standards Track doc (ISSN 2070-1721) published in May 2024. Three authors: K. Davis from Cisco Systems, B. Peabody from Uncloud, and P. Leach from the University of Washington. It obsoletes RFC 4122, which means this is the new official version of the UUID spec ๐Ÿ’… It doesn't list any RFC it updates.

Abstract

Okay so, this spec defines UUIDs (Universally Unique IDentifiers), also called GUIDs (Globally Unique IDentifiers), plus a Uniform Resource Name namespace for them ๐ŸŒˆ A UUID is 128 bits long and is meant to guarantee uniqueness across space and time, which is honestly iconic. UUIDs started out in the Apollo Network Computing System (NCS), then moved into the Open Software Foundation's (OSF's) Distributed Computing Environment (DCE), and then landed in Microsoft Windows platforms ๐ŸชŸ

This spec is derived from the OSF DCE spec, used with the kind permission of the OSF (now called "The Open Group") ๐Ÿ™ Info from earlier versions of the OSF DCE spec has been folded in too. And yes, this document obsoletes RFC 4122.

Status of This Memo

This is an Internet Standards Track document ๐ŸŽ€

It's a product of the Internet Engineering Task Force (IETF) and represents the consensus of the IETF community ๐Ÿซถ It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). If you want more info on Internet Standards, check Section 2 of RFC 7841 ๐Ÿ“š

Info about the current status of this document, any errata, and how to send feedback is at https://www.rfc-editor.org/info/rfc9562 ๐Ÿ”—

Copyright (c) 2024 IETF Trust and the persons identified as the document authors. All rights reserved ๐Ÿ’œ

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they stand on the date of publication. Please read these carefully, because they explain your rights and restrictions for this document ๐Ÿ™ˆ Code Components pulled out of this document must include Revised BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as described in the Revised BSD License.

Table of Contents

1.  Introduction
2.  Motivation
  2.1.  Update Motivation
3.  Terminology
  3.1.  Requirements Language
  3.2.  Abbreviations
4.  UUID Format
  4.1.  Variant Field
  4.2.  Version Field
5.  UUID Layouts
  5.1.  UUID Version 1
  5.2.  UUID Version 2
  5.3.  UUID Version 3
  5.4.  UUID Version 4
  5.5.  UUID Version 5
  5.6.  UUID Version 6
  5.7.  UUID Version 7
  5.8.  UUID Version 8
  5.9.  Nil UUID
  5.10. Max UUID
6.  UUID Best Practices
  6.1.  Timestamp Considerations
  6.2.  Monotonicity and Counters
  6.3.  UUID Generator States
  6.4.  Distributed UUID Generation
  6.5.  Name-Based UUID Generation
  6.6.  Namespace ID Usage and Allocation
  6.7.  Collision Resistance
  6.8.  Global and Local Uniqueness
  6.9.  Unguessability
  6.10. UUIDs That Do Not Identify the Host
  6.11. Sorting
  6.12. Opacity
  6.13. DBMS and Database Considerations
7.  IANA Considerations
  7.1.  IANA UUID Subtype Registry and Registration
  7.2.  IANA UUID Namespace ID Registry and Registration
8.  Security Considerations
9.  References
  9.1.  Normative References
  9.2.  Informative References
Appendix A.  Test Vectors
  A.1.  Example of a UUIDv1 Value
  A.2.  Example of a UUIDv3 Value
  A.3.  Example of a UUIDv4 Value
  A.4.  Example of a UUIDv5 Value
  A.5.  Example of a UUIDv6 Value
  A.6.  Example of a UUIDv7 Value
Appendix B.  Illustrative Examples
  B.1.  Example of a UUIDv8 Value (Time-Based)
  B.2.  Example of a UUIDv8 Value (Name-Based)
Acknowledgements
Authors' Addresses

1. Introduction โœจ

This spec defines a Uniform Resource Name namespace for Universally Unique IDentifiers (UUIDs), also known as Globally Unique IDentifiers (GUIDs) ๐Ÿฆ‹ A UUID is 128 bits long and requires no central registration process, so it's basically self-serve.

UUIDs are everywhere in computing, no cap. They're the core identifier infrastructure for lots of operating systems like Microsoft Windows and apps like the Mozilla Web browser. In many cases they end up exposed in all kinds of non-standard ways ๐Ÿ‘€

This spec tries to standardize that practice as openly as possible, in a way that benefits the whole Internet ๐ŸŒธ The goal is to be a concise guide for anyone who wants to build services with UUIDs, whether that's alongside URNs [RFC8141] or on their own.

There's an ITU-T Recommendation and an ISO/IEC Standard [X667] derived from [RFC4122]. Both sets of specs are aligned and fully technically compatible, which is so satisfying ๐Ÿงš Nothing in this document should be read as overriding the DCE standards that defined UUIDs.

2. Motivation ๐Ÿ’–

One of the main reasons to use UUIDs is that no centralized authority is needed to administer them. (Two formats may use optional IEEE 802 Node IDs, but others don't.) So generation on demand can be totally automated and used for all sorts of purposes ๐Ÿค– The generation algorithm described here supports very high allocation rates, 10 million per second per machine or more if needed, so they could even be used as transaction IDs. Main character energy ๐Ÿ‘‘

UUIDs are a fixed size (128 bits), which is reasonably small compared to the alternatives. That makes them great for sorting, ordering, and hashing of all sorts, storing in databases, simple allocation, and just generally easy to program with ๐Ÿ’…

Since UUIDs are unique and persistent, they make excellent URNs ๐ŸŽ€ Being able to generate a new UUID without a registration process means UUIDs are one of the URNs with the lowest minting cost. Iykyk.

2.1. Update Motivation ๐Ÿ”„

So much has changed since UUIDs were first created ๐Ÿ˜ญ Modern apps need to create and use UUIDs as the primary identifier for all kinds of things in complex systems, including (but not limited to) database keys, file names, machine or system names, and identifiers for event-driven transactions.

One area where UUIDs have gotten really popular is database keys. That's because modern apps are way more distributed. In those cases, the "auto-increment" schemes databases often use just don't work well, since coordinating sequential numeric IDs across a network can turn into a huge burden. UUIDs can create unique, reasonably short values in distributed systems without any coordination, which makes them a great alternative. But UUID versions 1-5, which were originally defined by [RFC4122], are missing some other desirable traits, like:

  1. UUID versions that aren't time ordered, such as UUIDv4 (described in Section 5.4), have poor database-index locality. That means new values created in a row aren't close to each other in the index, so inserts have to happen at random locations. The performance hit on the common structures used for this (B-trees and their variants) can be dramatic ๐Ÿ“‰
  2. The 100-nanosecond Gregorian Epoch used in UUIDv1 timestamps (described in Section 5.1) is uncommon and hard to represent accurately using a standard number format like the one described in [IEEE754] ๐Ÿคฏ
  3. You need introspection or parsing to order by time sequence, instead of being able to do a simple byte-by-byte comparison.
  4. Privacy and network security issues come up when a Media Access Control (MAC) address is used in the node field of UUIDv1 ๐Ÿ™ˆ Exposed MAC addresses can be used as an attack surface to locate network interfaces and reveal other info about those machines (at minimum the manufacturer and potentially other details). Plus, with virtual machines and containers, uniqueness of the MAC address is no longer guaranteed.
  5. Many implementation details specified in [RFC4122] involved trade-offs that aren't possible to specify for all applications, and they aren't necessary to produce interoperable implementations.
  6. [RFC4122] didn't distinguish between the requirements for generating a UUID and the requirements for simply storing one, even though those are often different ๐Ÿ“ฆ

Because of all the issues above, lots of widely distributed database apps and big app vendors have tried to solve the problem of creating a better time-based, sortable unique identifier for use as a database key. That has led to numerous implementations over the past 10+ years, all solving the same problem in slightly different ways ๐ŸŒˆ

While preparing this spec, the following 16 different implementations were analyzed for trends in total ID length, bit layout, lexical formatting and encoding, timestamp type, timestamp format, timestamp accuracy, node format and components, collision handling, and multi-timestamp tick generation sequencing ๐Ÿ”

  1. [ULID]
  2. [LexicalUUID]
  3. [Snowflake]
  4. [Flake]
  5. [ShardingID]
  6. [KSUID]
  7. [Elasticflake]
  8. [FlakeID]
  9. [Sonyflake]
  10. [orderedUuid]
  11. [COMBGUID]
  12. [SID]
  13. [pushID]
  14. [XID]
  15. [ObjectID]
  16. [CUID]

After digging through these implementations and the issues above, this document was written. In it, new UUIDs are adapted to address those issues โœจ

[RFC4122] itself also needed an overhaul to cover a bunch of topics, including (but not limited to) the following:

  1. Implementing miscellaneous errata reports, mostly around bit-layout clarifications that were causing inconsistent implementations [Err1957], [Err3546], [Err4975], [Err4976], [Err5560], etc. ๐Ÿ› ๏ธ
  2. Decoupling the other UUID versions from the UUIDv1 bit layout, so fields like "time_hi_and_version" don't need to be referenced inside a UUID that isn't time based. This also means giving UUIDv3, UUIDv4, and UUIDv5 their own definition sections, similar to the one for UUIDv1 ๐Ÿงฉ
  3. Providing implementation best practices for the many real-world scenarios and corner cases seen in existing and prototype implementations ๐Ÿ’ก
  4. Addressing security best practices and considerations for the modern age as they relate to MAC addresses, hashing algorithms, secure randomness, and other topics ๐Ÿ”
  5. Giving implementations a standards-based option for implementation-specific and/or experimental UUID designs ๐Ÿงช
  6. Providing more test vectors that illustrate real UUIDs created according to the spec ๐Ÿฆ‹

3. Terminology ๐Ÿ“–

3.1. Requirements Language

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here ๐ŸŽ€

3.2. Abbreviations ๐Ÿงš

Here's the glossary of every abbreviation this doc uses, so you can keep up:

  • ABNF: Augmented Backus-Naur Form
  • CSPRNG: Cryptographically Secure Pseudorandom Number Generator
  • DBMS: Database Management System
  • IEEE: Institute of Electrical and Electronics Engineers
  • ITU: International Telecommunication Union
  • MAC: Media Access Control
  • MD5: Message Digest 5
  • MSB: Most Significant Bit
  • OID: Object Identifier
  • SHA: Secure Hash Algorithm
  • SHA-1: Secure Hash Algorithm 1 (with message digest of 160 bits)
  • SHA-3: Secure Hash Algorithm 3 (arbitrary size)
  • SHA-224: Secure Hash Algorithm 2 with message digest size of 224 bits
  • SHA-256: Secure Hash Algorithm 2 with message digest size of 256 bits
  • SHA-512: Secure Hash Algorithm 2 with message digest size of 512 bits
  • SHAKE: Secure Hash Algorithm 3 based on the KECCAK algorithm
  • URN: Uniform Resource Names
  • UTC: Coordinated Universal Time
  • UUID: Universally Unique Identifier
  • UUIDv1: Universally Unique Identifier version 1
  • UUIDv2: Universally Unique Identifier version 2
  • UUIDv3: Universally Unique Identifier version 3
  • UUIDv4: Universally Unique Identifier version 4
  • UUIDv5: Universally Unique Identifier version 5
  • UUIDv6: Universally Unique Identifier version 6
  • UUIDv7: Universally Unique Identifier version 7
  • UUIDv8: Universally Unique Identifier version 8

4. UUID Format โœจ

okay so a UUID is 16 octets (128 bits) in size ๐Ÿ’– The variant bits and the version bits (we'll get to those in the next sections) decide the finer structure inside it. For the layout and the numbering, bits count from 0 up to 127 and octets count from 0 up to 15 ๐Ÿฆ‹

Unless an application or presentation protocol says otherwise, every field gets encoded with the most significant byte first. That's called "network byte order" and it's giving consistency ๐ŸŒธ

When you save UUIDs to binary, you sequence all the fields in big-endian format. ngl there's a known caveat here: Microsoft's Component Object Model (COM) GUIDs use little-endian when they're saved. The details are in [MS_COM_GUID], but that discussion is outside the scope of this spec, so we're not going into it here ๐Ÿ™ˆ

UUIDs MAY be represented as binary data or as integers. When you're using them with URNs or showing them as text in apps, the go-to format is the "hex-and-dash" string: groups of hexadecimal alphanumerics, uppercase or lowercase, split up by single dashes/hyphens ๐Ÿ’… If you're storing them in databases, check out Section 6.13 โœจ

The formal definition of the string representation is written in ABNF [RFC5234]:

UUID     = 4hexOctet "-"
           2hexOctet "-"
           2hexOctet "-"
           2hexOctet "-"
           6hexOctet
hexOctet = HEXDIG HEXDIG
DIGIT    = %x30-39
HEXDIG   = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"

The alphabetic characters can be all uppercase, all lowercase, or mixed case, per Section 2.3 of [RFC5234]. Here's an example UUID in that textual form ๐ŸŽ€

f81d4fae-7dec-11d0-a765-00a0c91e6bf6

Figure 1: Example String UUID Format

The exact same UUID from Figure 1 shows up in binary (Figure 2), as an unsigned integer (Figure 3), and as a URN (Figure 4), which is defined by [RFC8141] ๐Ÿงš

111110000001110101001111101011100111110111101100000100011101000\
01010011101100101000000001010000011001001000111100110101111110110

Figure 2: Example Binary UUID

329800735698586629295641978511506172918

Figure 3: Example Unsigned Integer UUID (Shown as a Decimal Number)

urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6

Figure 4: Example URN Namespace for UUID

There are loads of other ways to define a UUID format too! The examples below are not an exhaustive list, and they're only here for informational purposes ๐Ÿ‘‘
  • Some UUID implementations, such as those found in [Python] and [Microsoft], output the UUID in string format, dashes included, wrapped in curly braces ๐Ÿ“
  • [X667] provides UUID format definitions for using UUIDs with an OID ๐Ÿ’œ
  • [IBM_NCS] is a legacy implementation that produces a unique UUID format compatible with Variant 0xx of Table 1 ๐ŸŒˆ

4.1. Variant Field ๐Ÿฆ‹

The variant field decides the layout of the UUID. Basically, how you read every other bit depends on what's set in the variant field. It could more accurately be called a "type" field, but we're keeping the original term for compatibility ๐Ÿ’– The variant field is a variable number of the most significant bits of octet 8 of the UUID.

Table 1 shows what goes in the variant field. The letter "x" means "don't-care" ๐Ÿซถ

  +======+======+======+======+=========+=========================+
  | MSB0 | MSB1 | MSB2 | MSB3 | Variant | Description             |
  +======+======+======+======+=========+=========================+
  | 0    | x    | x    | x    | 1-7     | Reserved.  Network      |
  |      |      |      |      |         | Computing System (NCS)  |
  |      |      |      |      |         | backward compatibility, |
  |      |      |      |      |         | and includes Nil UUID   |
  |      |      |      |      |         | as per Section 5.9.     |
  +------+------+------+------+---------+-------------------------+
  | 1    | 0    | x    | x    | 8-9,A-B | The variant specified   |
  |      |      |      |      |         | in this document.       |
  +------+------+------+------+---------+-------------------------+
  | 1    | 1    | 0    | x    | C-D     | Reserved.  Microsoft    |
  |      |      |      |      |         | Corporation backward    |
  |      |      |      |      |         | compatibility.          |
  +------+------+------+------+---------+-------------------------+
  | 1    | 1    | 1    | x    | E-F     | Reserved for future     |
  |      |      |      |      |         | definition and includes |
  |      |      |      |      |         | Max UUID as per         |
  |      |      |      |      |         | Section 5.10.           |
  +------+------+------+------+---------+-------------------------+

Table 1: UUID Variants

Interoperability with variants other than the one defined here, in any form, isn't guaranteed. It's not likely to be an issue in practice though ๐ŸŒธ

Specifically for UUIDs in this document, bits 64 and 65 of the UUID (bits 0 and 1 of octet 8) MUST be set to 1 and 0 as specified in row 2 of Table 1. That's the main character rule here, so every bit and field layout avoids using these bits ๐Ÿ‘‘

4.2. Version Field โœจ

okay so the version number lives in the most significant 4 bits of octet 6, which is bits 48 through 51 of the UUID ๐Ÿฆ‹

Table 2 is the whole cast of versions for this UUID variant 10xx that this document defines, so let's look at it ๐Ÿ‘€

+======+======+======+======+=========+============================+
| MSB0 | MSB1 | MSB2 | MSB3 | Version | Description                |
+======+======+======+======+=========+============================+
| 0    | 0    | 0    | 0    | 0       | Unused.                    |
+------+------+------+------+---------+----------------------------+
| 0    | 0    | 0    | 1    | 1       | The Gregorian time-based   |
|      |      |      |      |         | UUID specified in this     |
|      |      |      |      |         | document.                  |
+------+------+------+------+---------+----------------------------+
| 0    | 0    | 1    | 0    | 2       | Reserved for DCE Security  |
|      |      |      |      |         | version, with embedded     |
|      |      |      |      |         | POSIX UUIDs.               |
+------+------+------+------+---------+----------------------------+
| 0    | 0    | 1    | 1    | 3       | The name-based version     |
|      |      |      |      |         | specified in this document |
|      |      |      |      |         | that uses MD5 hashing.     |
+------+------+------+------+---------+----------------------------+
| 0    | 1    | 0    | 0    | 4       | The randomly or            |
|      |      |      |      |         | pseudorandomly generated   |
|      |      |      |      |         | version specified in this  |
|      |      |      |      |         | document.                  |
+------+------+------+------+---------+----------------------------+
| 0    | 1    | 0    | 1    | 5       | The name-based version     |
|      |      |      |      |         | specified in this document |
|      |      |      |      |         | that uses SHA-1 hashing.   |
+------+------+------+------+---------+----------------------------+
| 0    | 1    | 1    | 0    | 6       | Reordered Gregorian time-  |
|      |      |      |      |         | based UUID specified in    |
|      |      |      |      |         | this document.             |
+------+------+------+------+---------+----------------------------+
| 0    | 1    | 1    | 1    | 7       | Unix Epoch time-based UUID |
|      |      |      |      |         | specified in this          |
|      |      |      |      |         | document.                  |
+------+------+------+------+---------+----------------------------+
| 1    | 0    | 0    | 0    | 8       | Reserved for custom UUID   |
|      |      |      |      |         | formats specified in this  |
|      |      |      |      |         | document.                  |
+------+------+------+------+---------+----------------------------+
| 1    | 0    | 0    | 1    | 9       | Reserved for future        |
|      |      |      |      |         | definition.                |
+------+------+------+------+---------+----------------------------+
| 1    | 0    | 1    | 0    | 10      | Reserved for future        |
|      |      |      |      |         | definition.                |
+------+------+------+------+---------+----------------------------+
| 1    | 0    | 1    | 1    | 11      | Reserved for future        |
|      |      |      |      |         | definition.                |
+------+------+------+------+---------+----------------------------+
| 1    | 1    | 0    | 0    | 12      | Reserved for future        |
|      |      |      |      |         | definition.                |
+------+------+------+------+---------+----------------------------+
| 1    | 1    | 0    | 1    | 13      | Reserved for future        |
|      |      |      |      |         | definition.                |
+------+------+------+------+---------+----------------------------+
| 1    | 1    | 1    | 0    | 14      | Reserved for future        |
|      |      |      |      |         | definition.                |
+------+------+------+------+---------+----------------------------+
| 1    | 1    | 1    | 1    | 15      | Reserved for future        |
|      |      |      |      |         | definition.                |
+------+------+------+------+---------+----------------------------+

Table 2: UUID Variant 10xx Versions Defined by This Specification

an example version/variant layout for UUIDv4 comes right after the table ๐ŸŽ€ here the "M" is where the version goes (0x4, which is 0b0100), and the "N" is where the variant goes. for variant 10xx there are four possible hex values: 0x8 (0b1000), 0x9 (0b1001), 0xA (0b1010), 0xB (0b1011) ๐Ÿ’œ

00000000-0000-4000-8000-000000000000
00000000-0000-4000-9000-000000000000
00000000-0000-4000-A000-000000000000
00000000-0000-4000-B000-000000000000
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

Figure 5: UUIDv4 Variant Examples

ngl the other UUID variants from Table 1 use different sub-typing or versioning tricks ๐Ÿงš the recording and definition of those remaining variant and sub-typing combos are outside the scope of this document, so that's not our problem here ๐Ÿ’…


5. UUID Layouts ๐ŸŒˆ

okay bestie, so to avoid confusion about which bit lives where, inside octets and across versions, the UUID record gets laid out as a grouping of fields in a bit layout with four octets per row ๐Ÿ“ the fields are shown with the most significant one first โœจ

5.1. UUID Version 1 โฐ

UUIDv1 is a time-based UUID with a 60-bit timestamp. It's counted in Coordinated Universal Time (UTC) as a count of 100-nanosecond intervals since 00:00:00.00, 15 October 1582, which is the date of the Gregorian reform to the Christian calendar ๐Ÿ“…๐ŸŒธ

UUIDv1 also has a clock sequence field, and it's there to help avoid duplicates that could pop up when the clock gets set backwards in time, or if the Node ID changes ๐Ÿ™ˆ

the node field is an IEEE 802 MAC address, usually the host address, or a randomly derived value per Sections 6.9 and 6.10 ๐Ÿซถ

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           time_low                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           time_mid            |  ver  |       time_high       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var|         clock_seq         |             node              |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                              node                             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 6: UUIDv1 Field and Bit Layout

here's the field breakdown, bestie ๐Ÿ“

  • time_low: the least significant 32 bits of the 60-bit starting timestamp. It sits in bits 0 through 31 (octets 0-3) ๐ŸŒท
  • time_mid: the middle 16 bits of the 60-bit starting timestamp. It sits in bits 32 through 47 (octets 4-5) ๐ŸŒผ
  • ver: the 4-bit version field as defined by Section 4.2, set to 0b0001 (1). It sits in bits 48 through 51 of octet 6 โœจ
  • time_high: the least significant 12 bits from the 60-bit starting timestamp. It sits in bits 52 through 63 (octets 6-7) ๐Ÿ’ซ
  • var: the 2-bit variant field as defined by Section 4.1, set to 0b10. It sits in bits 64 and 65 of octet 8 ๐Ÿฆ‹
  • clock_seq: the 14 bits holding the clock sequence. They sit in bits 66 through 79 (octets 8-9) ๐ŸŽ€
  • node: a 48-bit spatially unique identifier. It sits in bits 80 through 127 (octets 10-15) ๐Ÿ’–

if a system doesn't have UTC but does have local time, it MAY use that instead, as long as it does so consistently throughout the system. it's not recommended though, because generating UTC from local time only needs a time-zone offset, so it's not a big deal to just do it right ๐Ÿ™„

if the clock gets set backwards, or might have been set backwards (like if the system was powered off), and the UUID generator can't be sure that no UUIDs were generated with timestamps larger than the value the clock was set to, then the clock sequence MUST be changed ๐Ÿ˜ญ if the previous clock sequence value is known, it MAY be incremented. otherwise it SHOULD be set to a random or high-quality pseudorandom value ๐ŸŒˆ

same vibe if the Node ID changes (like a network card got moved between machines). setting the clock sequence to a random number minimizes the chance of a duplicate caused by small differences in the machines' clock settings ๐Ÿงš if the clock sequence tied to the changed Node ID were known, then it MAY be incremented, but that's pretty unlikely ๐Ÿ’…

the clock sequence MUST be originally initialized (that is, once in the lifetime of a system) to a random number, so the correlation across systems stays low ๐Ÿ”ฅ this gives maximum protection against Node IDs that move or switch from system to system rapidly ๐Ÿƒโ€โ™€๏ธ the initial value MUST NOT be correlated to the Node ID ๐Ÿ™…โ€โ™€๏ธ

notes about nodes derived from IEEE 802 (these are important, bestie ๐Ÿ‘‘):

  • on systems with multiple IEEE 802 addresses, any available one MAY be used ๐Ÿ’œ
  • on systems with no IEEE address, a randomly or pseudorandomly generated value MUST be used; see Sections 6.9 and 6.10 ๐ŸŒธ
  • on systems using a 64-bit MAC address, the least significant, rightmost 48 bits MAY be used ๐Ÿฆ‹
  • systems using an IEEE 802.15.4 16-bit address SHOULD instead use their 64-bit MAC address, where the least significant, rightmost 48 bits MAY be used. an alternative is to generate 32 bits of random data and postfix them at the end of the 16-bit MAC address to create a 48-bit value ๐Ÿ“

5.2. UUID Version 2 ๐ŸŽ€

UUIDv2 is for DCE Security UUIDs (see [C309] and [C311]) ๐Ÿ’… as such, the definition of these UUIDs is outside the scope of this specification, so we're leaving it to them ๐Ÿซถ

that's the end of this part, bestie, more coming up next ๐Ÿ’œโœจ

5.3. UUID Version 3 ๐ŸŽ€

UUIDv3 is your go-to for making UUIDs from names that live in some namespace and are unique inside it, see Section 6.5 ๐ŸŒธ

UUIDv3 values are made by running an MD5 hash [RFC1321] over a Namespace ID value (Section 6.6) glued together with the name you want. Before that happens, both pieces get turned into a canonical sequence of octets, following the standards or conventions of their namespace, in network byte order โœจ That MD5 value then fills all 128 bits of the UUID layout, and the version and variant swap in over their bits as defined by Sections 4.2 and 4.1. Appendix A.2 has an example of this bit swap ๐Ÿ’…

Want the scoop on picking a name's canonical format? Check Section 6.5 under the heading "A note on names" ๐Ÿ“–

Okay, real talk: where possible, UUIDv5 SHOULD be used in lieu of UUIDv3 ๐Ÿซถ For more on MD5 security, see [RFC6151] ๐Ÿ’œ

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                            md5_high                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          md5_high             |  ver  |       md5_mid         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |var|                        md5_low                            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                            md5_low                            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 7: UUIDv3 Field and Bit Layout ๐Ÿฆ‹

md5_high: The first 48 bits of the layout are filled with the most significant, leftmost 48 bits from the computed MD5 value. Occupies bits 0 through 47 (octets 0-5).

ver: The 4-bit version field as defined by Section 4.2, set to 0b0011 (3). Occupies bits 48 through 51 of octet 6.

md5_mid: 12 more bits of the layout consisting of the least significant, rightmost 12 bits of 16 bits immediately following md5_high from the computed MD5 value. Occupies bits 52 through 63 (octets 6-7).

var: The 2-bit variant field as defined by Section 4.1, set to 0b10. Occupies bits 64 and 65 of octet 8.

md5_low: The final 62 bits of the layout immediately following the var field to be filled with the least significant, rightmost bits of the final 64 bits from the computed MD5 value. Occupies bits 66 through 127 (octets 8-15)


5.4. UUID Version 4 ๐ŸŒˆ

UUIDv4 is meant for generating UUIDs from truly random or pseudorandom numbers ๐ŸŽฒ

An implementation may generate 128 bits of random data that is used to fill out the UUID fields in Figure 8. The version and variant then replace the respective bits as defined by Sections 4.1 and 4.2 ๐Ÿ’–

Alternatively, an implementation MAY choose to randomly generate the exact required number of bits for random_a, random_b, and random_c (122 bits total) and then concatenate the version and variant in the required position ๐Ÿงš

For guidelines on random data generation, see Section 6.9 โœจ

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           random_a                            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          random_a             |  ver  |       random_b        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |var|                       random_c                            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           random_c                            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 8: UUIDv4 Field and Bit Layout ๐Ÿฆ‹

random_a: The first 48 bits of the layout that can be filled with random data as specified in Section 6.9. Occupies bits 0 through 47 (octets 0-5).

ver: The 4-bit version field as defined by Section 4.2, set to 0b0100 (4). Occupies bits 48 through 51 of octet 6.

random_b: 12 more bits of the layout that can be filled random data as per Section 6.9. Occupies bits 52 through 63 (octets 6-7).

var: The 2-bit variant field as defined by Section 4.1, set to 0b10. Occupies bits 64 and 65 of octet 8.

random_c: The final 62 bits of the layout immediately following the var field to be filled with random data as per Section 6.9. Occupies bits 66 through 127 (octets 8-15).


5.5. UUID Version 5 ๐Ÿ”ฅ

UUIDv5 is meant for generating UUIDs from "names" that are drawn from, and unique within, some "namespace" as per Section 6.5 ๐Ÿ’œ

UUIDv5 values are created by computing an SHA-1 hash [FIPS180-4] over a given Namespace ID value (Section 6.6) concatenated with the desired name value after both have been converted to a canonical sequence of octets, as defined by the standards or conventions of its namespace, in network byte order. The most significant, leftmost 128 bits of the SHA-1 value are then used to populate all 128 bits of the UUID layout, and the remaining 32 least significant, rightmost bits of SHA-1 output are discarded. The UUID version and variant then replace the respective bits as defined by Sections 4.2 and 4.1. An example of this bit substitution and discarding excess bits can be found in Appendix A.4 ๐Ÿซง

Info on picking a name's canonical format within a namespace lives in Section 6.5 under the heading "A note on names" ๐Ÿ“–

There may be scenarios, usually depending on organizational security policies, where SHA-1 libraries may not be available or may be deemed unsafe for use. As such, it may be desirable to generate name-based UUIDs derived from SHA-256 or newer SHA methods. These name-based UUIDs MUST NOT utilize UUIDv5 and MUST be within the UUIDv8 space defined by Section 5.8. An illustrative example of UUIDv8 for SHA-256 name-based UUIDs is provided in Appendix B.2 ๐Ÿ“

For more information on SHA-1 security considerations, see [RFC6194] ๐Ÿ™ˆ

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           sha1_high                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         sha1_high             |  ver  |      sha1_mid         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |var|                       sha1_low                            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           sha1_low                            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 9: UUIDv5 Field and Bit Layout ๐Ÿฆ‹

sha1_high: The first 48 bits of the layout are filled with the most significant, leftmost 48 bits from the computed SHA-1 value. Occupies bits 0 through 47 (octets 0-5).

ver: The 4-bit version field as defined by Section 4.2, set to 0b0101 (5). Occupies bits 48 through 51 of octet 6.

sha1_mid: 12 more bits of the layout consisting of the least significant, rightmost 12 bits of 16 bits immediately following sha1_high from the computed SHA-1 value. Occupies bits 52 through 63 (octets 6-7).

var: The 2-bit variant field as defined by Section 4.1, set to 0b10. Occupies bits 64 and 65 of octet 8.

sha1_low: The final 62 bits of the layout immediately following the var field to be filled by skipping the two most significant, leftmost bits of the remaining SHA-1 hash and then using the next 62 most significant, leftmost bits. Any leftover SHA-1 bits are discarded and unused. Occupies bits 66 through 127 (octets 8-15).


5.6. UUID Version 6 ๐Ÿ‘

UUIDv6 is a field-compatible version of UUIDv1 (Section 5.1), reordered for improved DB locality, so it plays nicer with databases ๐Ÿ—‚๏ธ It is expected that UUIDv6 will primarily be implemented in contexts where UUIDv1 is used. Systems that do not involve legacy UUIDv1 SHOULD use UUIDv7 (Section 5.7) instead ๐ŸŒท

Instead of splitting the timestamp into the low, mid, and high sections from UUIDv1, UUIDv6 changes this sequence so timestamp bytes are stored from most to least significant. That is, given a 60-bit timestamp value as specified for UUIDv1 in Section 5.1, for UUIDv6 the first 48 most significant bits are stored first, followed by the 4-bit version (same position), followed by the remaining 12 bits of the original 60-bit timestamp โฐ

The clock sequence and node bits remain unchanged from their position in Section 5.1 ๐Ÿงฉ

The clock sequence and node bits SHOULD be reset to a pseudorandom value for each new UUIDv6 generated; however, implementations MAY choose to retain the old clock sequence and MAC address behavior from Section 5.1. For more information on MAC address usage within UUIDs, see the Section 8 ๐Ÿ™ˆ

The format for the 16-byte, 128-bit UUIDv6 is shown in Figure 10 ๐Ÿ’…

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                           time_high                           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           time_mid            |  ver  |       time_low        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |var|         clock_seq         |             node              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                              node                             |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 10: UUIDv6 Field and Bit Layout ๐Ÿฆ‹

time_high: The most significant 32 bits of the 60-bit starting timestamp. Occupies bits 0 through 31 (octets 0-3).

time_mid: The middle 16 bits of the 60-bit starting timestamp. Occupies bits 32 through 47 (octets 4-5).

ver: The 4-bit version field as defined by Section 4.2, set to 0b0110 (6). Occupies bits 48 through 51 of octet 6.

time_low: 12 bits that will contain the least significant 12 bits from the 60-bit starting timestamp. Occupies bits 52 through 63 (octets 6-7).

var: The 2-bit variant field as defined by Section 4.1, set to 0b10. Occupies bits 64 and 65 of octet 8.

clock_seq: The 14 bits containing the clock sequence. Occupies bits 66 through 79 (octets 8-9).

node: 48-bit spatially unique identifier. Occupies bits 80 through 127 (octets 10-15).

With UUIDv6, the steps for splitting the timestamp into time_high and time_mid are OPTIONAL since the 48 bits of time_high and time_mid will remain in the same order. An extra step of splitting the first 48 bits of the timestamp into the most significant 32 bits and least significant 16 bits proves useful when reusing an existing UUIDv1 implementation ๐Ÿซถ

5.7. UUID Version 7 ๐Ÿฆ‹

okay so UUIDv7 is giving main character energy ๐Ÿ’– It has a time-ordered value field built from the super well-known Unix Epoch timestamp, aka the number of milliseconds since midnight 1 Jan 1970 UTC, with leap seconds excluded โœจ Generally, UUIDv7 has improved entropy characteristics over UUIDv1 (Section 5.1) or UUIDv6 (Section 5.6). no cap, it's a glow-up ๐Ÿ’…

UUIDv7 values get made by putting a Unix timestamp in milliseconds into the most significant 48 bits, then filling the remaining 74 bits (the required version and variant bits don't count) with random bits for each new UUIDv7 generated, so they stay unique as per Section 6.9. Alternatively, implementations MAY fill those 74 bits, jointly, with a combo of the following subfields, in this order from the most significant bits to the least, to guarantee additional monotonicity within a millisecond ๐ŸŒˆ

  1. An OPTIONAL sub-millisecond timestamp fraction (12 bits at maximum) as per Section 6.2 (Method 3).
  2. An OPTIONAL carefully seeded counter as per Section 6.2 (Method 1 or 2).
  3. Random data for each new UUIDv7 generated for any remaining space.

Implementations SHOULD utilize UUIDv7 instead of UUIDv1 and UUIDv6 if possible ๐ŸŒธ

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           unix_ts_ms                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          unix_ts_ms           |  ver  |       rand_a          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var|                        rand_b                             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                            rand_b                             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 11: UUIDv7 Field and Bit Layout

unix_ts_ms: the 48-bit big-endian unsigned number of the Unix Epoch timestamp in milliseconds as per Section 6.1. It occupies bits 0 through 47 (octets 0-5).

ver: the 4-bit version field as defined by Section 4.2, set to 0b0111 (7). It occupies bits 48 through 51 of octet 6.

rand_a: 12 bits of pseudorandom data to provide uniqueness as per Section 6.9 and/or optional constructs to guarantee additional monotonicity as per Section 6.2. It occupies bits 52 through 63 (octets 6-7).

var: the 2-bit variant field as defined by Section 4.1, set to 0b10. It occupies bits 64 and 65 of octet 8.

rand_b: the final 62 bits of pseudorandom data to provide uniqueness as per Section 6.9 and/or an optional counter to guarantee additional monotonicity as per Section 6.2. It occupies bits 66 through 127 (octets 8-15).

5.8. UUID Version 8 ๐Ÿ”ฅ

UUIDv8 provides a format for experimental or vendor-specific use cases, so it's the "make it your own" option ๐Ÿงš The only requirement is that the variant and version bits MUST be set as defined in Sections 4.1 and 4.2. UUIDv8's uniqueness will be implementation specific and MUST NOT be assumed. so don't just assume it's unique, babe ๐Ÿ™ˆ

The only explicitly defined bits are those of the version and variant fields, which leaves 122 bits for implementation-specific UUIDs. To be clear, UUIDv8 is not a replacement for UUIDv4 (Section 5.4), where all 122 extra bits are filled with random data ๐Ÿ’œ

Some example situations where UUIDv8 usage could happen:

  • An implementation would like to embed extra information within the UUID other than what is defined in this document.
  • An implementation has other application and/or language restrictions that inhibit the use of one of the current UUIDs.

Appendix B provides two illustrative examples of custom UUIDv8 algorithms to address two example scenarios ๐Ÿ“

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           custom_a                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          custom_a             |  ver  |       custom_b        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var|                       custom_c                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           custom_c                            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Figure 12: UUIDv8 Field and Bit Layout

custom_a: the first 48 bits of the layout, which can be filled as an implementation sees fit. It occupies bits 0 through 47 (octets 0-5).

ver: the 4-bit version field as defined by Section 4.2, set to 0b1000 (8). It occupies bits 48 through 51 of octet 6.

custom_b: 12 more bits of the layout, which can be filled as an implementation sees fit. It occupies bits 52 through 63 (octets 6-7).

var: the 2-bit variant field as defined by Section 4.1, set to 0b10. It occupies bits 64 and 65 of octet 8.

custom_c: the final 62 bits of the layout, immediately following the var field, to be filled as an implementation sees fit. It occupies bits 66 through 127 (octets 8-15).

5.9. Nil UUID ๐Ÿซง

The Nil UUID is a special form of UUID that is specified to have all 128 bits set to zero. It's the "zero vibes" one ๐Ÿ™ƒ

00000000-0000-0000-0000-000000000000

Figure 13: Nil UUID Format

A Nil UUID value can be useful for communicating the absence of any other UUID value in situations that otherwise require or use a 128-bit UUID. A Nil UUID can express the concept "no such value here" โœจ Thus, it is reserved for such use as needed for implementation-specific situations.

Note that the Nil UUID value falls within the range of the Apollo NCS variant as per the first row of Table 1 rather than the variant defined by this document. ๐Ÿ™ˆ

5.10. Max UUID ๐Ÿ‘‘

The Max UUID is a special form of UUID that is specified to have all 128 bits set to 1. This UUID can be thought of as the inverse of the Nil UUID defined in Section 5.9. Nil's total opposite, iykyk ๐ŸŒˆ

FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF

Figure 14: Max UUID Format

A Max UUID value can be used as a sentinel value in situations where a 128-bit UUID is required, but a concept such as "end of UUID list" needs to be expressed, and it is reserved for such use as needed for implementation-specific situations ๐Ÿงš

Note that the Max UUID value falls within the range of the "yet-to-be defined" future UUID variant as per the last row of Table 1 rather than the variant defined by this document. ๐Ÿ’€

6. UUID Best Practices ๐ŸŽ€

The minimum requirements for generating UUIDs of each version are described in this document. Everything else is an implementation detail, and it is up to the implementer to decide what is appropriate for a given implementation. Various relevant factors are covered below to help guide an implementer through the different trade-offs among differing UUID implementations ๐Ÿ’…

6.1. Timestamp Considerations โฐ

Okay bestie, the timestamp is the part you really have to get right ๐Ÿ’… UUIDv7 was a huge debate while this spec was being made (people were ARGUING ๐Ÿ˜ญ), and picking the right timestamp for your app is so important. This section covers the most common points ๐ŸŒธ

Reliability: Implementations grab the current timestamp from a reliable source so the values stay time ordered and always go up โœจ Watch out for timestamp changes coming from the environment or the OS, and handle them in a way that matches the implementation requirements. Like, if the system clock can move backward (manual adjustment, or a time sync protocol correcting it), implementations need to figure out what they're gonna do in that case ๐Ÿ™ˆ (See "Altering, Fuzzing, or Smearing" below.)

Source: UUIDv1 and UUIDv6 both use a Gregorian Epoch timestamp, while UUIDv7 goes with a Unix Epoch timestamp ๐Ÿ•ฐ๏ธ If you need some other timestamp source or a custom Epoch, UUIDv8 MUST be used ๐Ÿ‘‘

Sub-second Precision and Accuracy: There are sooo many precision levels: milliseconds, microseconds, nanoseconds, and beyond ๐Ÿคฏ You might also want fractional sub-second values so you can mix precisions in a time-ordered way. Also, system clocks have their own underlying granularity, which is often coarser than what the OS offers ๐Ÿ’€

With UUIDv1 and UUIDv6 you get 100 nanoseconds of precision ๐Ÿ”ฅ, while UUIDv7 has millisecond precision by default in the Unix Epoch. That doesn't go past what most modern systems can actually handle โœจ For other precision levels, UUIDv8 has your back ๐Ÿฆ‹

Kinda like Section 6.2: with UUIDv1 or UUIDv6, you can simulate a high-resolution timestamp by keeping a count of how many UUIDs were generated with the same system time value, then using that count to fill the low order bits of the timestamp ๐Ÿงš The high-resolution count goes from zero up to the number of 100-nanosecond intervals per system-time interval.

Length: The length of the timestamp decides how many ticks fit in a UUID before the timestamp field hits its max value, so pick the right length for your timestamp ๐ŸŽ€ UUIDv1 and UUIDv6 use a 60-bit timestamp that's good until 5623 AD (that's a long time away, bestie ๐Ÿ˜ญ). UUIDv7 has a 48-bit timestamp valid until the year 10889 AD ๐ŸŒˆ

Altering, Fuzzing, or Smearing: Implementations MAY alter the actual timestamp ๐Ÿ™ƒ Examples include security considerations around putting a real-clock value into a UUID to 1) correct inaccurate clocks, 2) handle leap seconds, or 3) get a millisecond value by dividing by 1024 (or some other number) for performance reasons, instead of dividing microseconds by 1000 โšก This spec makes no requirement or guarantee about how close the clock value has to be to the actual time ๐Ÿคทโ€โ™€๏ธ If UUIDs don't need to be generated super often, the UUIDv1 or UUIDv6 timestamp can just be the system time multiplied by the number of 100-nanosecond intervals per system-time interval ๐Ÿ’œ

Padding: When you need timestamp padding, implementations MUST pad the most significant bits (leftmost) with data ๐Ÿซถ For example, fill the most significant, leftmost bits of a Unix timestamp with zeroes to finish the 48-bit timestamp in UUIDv7. Another option is filling the most significant, leftmost bits with the number of 32-bit Unix timestamp rollovers after 2038-01-19 ๐ŸŒธ

Truncating: When timestamps need truncating, the lower, least significant bits MUST be used โœ‚๏ธ For example, truncating a 64-bit Unix timestamp down to the least significant, rightmost 48 bits for UUIDv7 ๐Ÿ’…

Error Handling: If a system overruns the generator by requesting too many UUIDs within a single system-time interval, the UUID service can return an error or stall the UUID generator until the system clock catches up. It MUST NOT knowingly return duplicate values due to a counter rollover ๐Ÿšจ Note that if processors overrun UUID generation frequently, additional Node IDs can be allocated to the system. That permits higher speed allocation by making multiple UUIDs potentially available for each timestamp value ๐Ÿ“ Similar techniques are discussed in Section 6.4 โœจ

6.2. Monotonicity and Counters ๐Ÿฆ‹

okay so monotonicity (each new value being greater than the last one) is literally the backbone of time-based sortable UUIDs ๐Ÿ’… normally, time-based UUIDs from this document will be monotonic because of the embedded timestamp โœจ but implementations can guarantee extra monotonicity with the stuff in this section ๐Ÿซถ

take care to make sure UUIDs generated in batches are also monotonic ๐ŸŽ€ like, if one thousand UUIDs get made for the same timestamp, there should be enough logic to keep track of the creation order of those one thousand UUIDs ๐Ÿ˜ญ batch UUID creation implementations MAY use a monotonic counter that goes up for each UUID created during a given timestamp ๐ŸŒธ

for single-node UUID implementations that don't need to make batches, the embedded timestamp in UUIDv6 and UUIDv7 can give you enough monotonicity just by making sure the timestamp goes up before you make a new UUID ๐Ÿงš distributed nodes get covered in Section 6.4 ๐Ÿ’œ

implementations SHOULD use the following methods for single-node UUID implementations that need batch UUID creation or are otherwise worried about monotonicity with high-frequency UUID generation ๐Ÿ‘‘

Fixed Bit-Length Dedicated Counter (Method 1) ๐Ÿ“ okay so some implementations set aside a specific number of bits in the UUID layout for the sole job of counting how many UUIDs got created during a given UUID timestamp tick ๐Ÿ’– if it's there, a fixed bit-length counter MUST be positioned immediately after the embedded timestamp ๐ŸŒˆ this helps with sortability and lets random data get generated for each counter increment โœจ with this method, the rand_a section (or a subset of its leftmost bits) of UUIDv7 gets used as a fixed bit-length dedicated counter that goes up for every UUID generation ๐Ÿฆ‹ the trailing random bits generated for each new UUID in rand_b can help make the UUIDs unguessable ๐Ÿ™ˆ if more counter bits are needed, the most significant (leftmost) bits of rand_b MAY be used as extra counter bits ๐Ÿ’…

Monotonic Random (Method 2) ๐ŸŒธ with this method, the random data also works as a counter ๐Ÿซถ this monotonic value can be thought of as a "randomly seeded counter" that MUST be incremented in the least significant position for each UUID created on a given timestamp tick ๐Ÿ”ฅ UUIDv7's rand_b section SHOULD be used with this method to handle batch UUID generation during a single timestamp tick ๐Ÿ’œ the increment value for every UUID generation is a random integer of any desired length larger than zero ๐ŸŽ€ that keeps the UUIDs at the level of unguessability the underlying entropy gives them โœจ the increment value MAY be 1 when how many UUIDs get generated in a given period of time matters and guessability isn't an issue ๐Ÿ“ but incrementing the counter by 1 SHOULD NOT be used by implementations that favor unguessability, because those values are super easy to guess ๐Ÿ˜ญ

Replace Leftmost Random Bits with Increased Clock Precision (Method 3) ๐Ÿงš for UUIDv7, which has millisecond timestamp precision, it's possible to use the extra clock precision the system has to stand in for up to 12 random bits right after the timestamp ๐ŸŒŸ this can give you values that are time ordered with sub-millisecond precision, using however many bits fit the implementation environment ๐Ÿ’– with this method, the additional time precision bits MUST follow the timestamp as the next available bit in the rand_a field for UUIDv7 ๐Ÿฆ‹

okay so to calculate this value, start with the portion of the timestamp expressed as a fraction of the clock's tick value (a fraction of a millisecond for UUIDv7) โœจ then count how many possible values fit in the available bit space, which is 4096 for the UUIDv7 rand_a field ๐Ÿ’… using floating point or scaled integer arithmetic, multiply that fraction of a millisecond by 4096 and round down (toward zero) to an integer ๐Ÿซถ that gives you a number between 0 and the max allowed for the bits you picked, and it sorts monotonically based on time ๐ŸŒˆ each increasing fractional value gives you an increasing bit field value, up to the precision those bits can hold ๐Ÿ‘‘

for example, let's say the system timestamp is 1 Jan 2023 12:34:56.1234567 ๐ŸŽ€ taking the precision past 1 ms gives you 0.4567 as a fraction of a millisecond ๐Ÿ™ˆ if we want to encode this in 12 bits, we take the count of possible values that fit in those bits (4096, or 2^12), multiply it by our millisecond fraction of 0.4567, and truncate the result to an integer, which gives 1870 ๐Ÿ“ in hexadecimal that's 0x74E, or the binary bits 0b011101001110 ๐Ÿ’œ you can then use those 12 bits as the most significant (leftmost) part of the random section of the UUID (like the rand_a field in UUIDv7) ๐Ÿฆ‹ this works for any bit length that fits in a UUID, and applications can pick the right length based on how much clock precision they have ๐ŸŒธ but for UUIDv7 it's capped at 12 bits max so there's still enough room for random bits ๐Ÿ’…

the main benefit of encoding extra timestamp precision is that it uses time precision the system clock already has to give you values that are more likely to be unique ๐Ÿงš so it can simplify some implementations โœจ this technique can also be used alongside one of the other methods, where this extra time precision would come right after the timestamp ๐Ÿซถ then, if any bits are going to be used as a clock sequence, they'd follow next ๐ŸŒˆ

the following sub-topics cover issues that are only about making reliable fixed bit-length dedicated counters ๐Ÿ‘‘

Fixed Bit-Length Dedicated Counter Seeding ๐ŸŽ€ implementations using the fixed bit-length counter method randomly initialize the counter with each new timestamp tick ๐Ÿ’– but when the timestamp hasn't gone up, the counter gets incremented by the desired increment logic instead ๐ŸŒŸ when you use a randomly seeded counter alongside Method 1, the random value MAY be regenerated with each counter increment without messing up sortability ๐Ÿ™ˆ the downside is that Method 1 is prone to overflows if you don't pick a counter of adequate length, or if the random data leaves little room for the number of increments you need ๐Ÿ˜ญ implementations using the fixed bit-length counter method MAY also choose to randomly initialize just a portion of the counter instead of the whole thing ๐Ÿ’… for example, a 24-bit counter could have its 23 least significant, rightmost bits randomly initialized ๐Ÿ“ the remaining most significant, leftmost counter bit is initialized as zero, purely to guard against counter rollovers ๐Ÿฆ‹

Fixed Bit-Length Dedicated Counter Length ๐Ÿงš pick a counter bit-length that can properly handle the level of timestamp precision you're using ๐ŸŒธ for example, millisecond precision generally needs a bigger counter than a timestamp with nanosecond precision ๐Ÿ’œ general guidance is that the counter SHOULD be at least 12 bits but no longer than 42 bits ๐Ÿ‘‘ be careful that the counter length you pick leaves enough entropy in the random portion of the UUID after the counter โœจ that entropy helps keep the UUIDs made within the batch unguessable ๐Ÿซถ

the following sub-topics cover rollover handling with either type of counter method ๐ŸŒˆ

Counter Rollover Guards ๐Ÿ’– the technique from "Fixed Bit-Length Dedicated Counter Seeding" above, which describes setting aside a segment of the fixed bit-length counter as a rollover guard, also helps mitigate counter rollover issues ๐Ÿฆ‹ the same technique works with monotonic random counter methods by making sure the total length of a possible increment in the least significant, rightmost position is less than the total length of the random value being incremented ๐ŸŒŸ that way the most significant, leftmost bits can get incremented as a rollover guard ๐Ÿ™ˆ

Counter Rollover Handling ๐Ÿ˜ญ counter rollovers MUST be handled by the application to avoid sorting issues ๐Ÿ’… the general guidance is that applications that care about absolute monotonicity and sortability should freeze the counter and wait for the timestamp to move forward, which makes sure monotonicity doesn't break ๐Ÿ“ alternatively, implementations MAY increment the timestamp ahead of the actual time and reinitialize the counter ๐Ÿฆ‹

implementations MAY use the following logic to make sure UUIDs with embedded counters are monotonic in nature ๐Ÿงš

  1. compare the current timestamp against the previously stored timestamp ๐ŸŒธ
  2. if the current timestamp is equal to the previous timestamp, increment the counter according to the desired method ๐Ÿ’œ
  3. if the current timestamp is greater than the previous timestamp, reinitialize the desired counter method for the new timestamp and generate new random bytes (if the bytes were frozen or being used as the seed for a monotonic counter) ๐Ÿ‘‘

Monotonic Error Checking ๐ŸŽ€ implementations SHOULD check if the UUID just generated is greater than the previously generated UUID ๐Ÿ’– if that's not the case, a bunch of things could have happened, like clock rollbacks, leap second handling, and counter rollovers ๐ŸŒŸ applications SHOULD have enough logic to catch these scenarios and fix the problem so the next UUID generated is greater than the previous one, or they should at least report an appropriate error ๐Ÿ™ˆ to handle this, the general guidance is that the application MAY reuse the previous timestamp and increment the previous counter method ๐Ÿ˜ญโœจ

6.3. UUID Generator States โœจ

Okay so the generator state is optional ๐Ÿซถ and it only has to be read from stable storage once, at boot time, if you load it into a system-wide shared volatile store (and update that copy whenever the stable store changes) ๐Ÿ’พโœจ

This stable storage MAY be used to record different bits of the UUID generation, which are super handy for batch UUID generation and for monotonic error checking with UUIDv6 and UUIDv7 ๐Ÿ”ฅ The stored values include, but aren't limited to, the last known timestamp, the clock sequence, counters, and random data ๐ŸŽ€

If an implementation has no stable store available, then it MAY proceed with UUID generation as if this were the first UUID created within a batch. ngl this is the least desirable setup ๐Ÿ˜ญ because it makes the clock sequence, counters, or random data get created way more often, and that raises the chance of duplicates. Frequent random number generation also puts more stress on the entropy source and/or entropy pool that the random numbers come from ๐Ÿ’€

An implementation MAY also return an application error when collision resistance is the utmost concern ๐Ÿซฃ What that error means is up to the application and implementation. Check out Section 6.7 for more on weighting collision tolerance in applications ๐Ÿงš

For UUIDv1 and UUIDv6, if the Node ID can never change (like when the network interface card the Node ID comes from is inseparable from the system), or if any change also re-initializes the clock sequence to a random value, then the current Node ID may be returned instead of keeping anything in stable store ๐ŸŒธ

Also for UUIDv1 and UUIDv6, the state doesn't have to be written to stable store every single time a UUID is made ๐Ÿ™ˆ The timestamp in the stable store can be set periodically to a value larger than any timestamp used so far. As long as the generated UUIDs have timestamps below that value, and the clock sequence and Node ID stay the same, only the shared volatile copy needs updating ๐Ÿ’– And if the timestamp in stable store is in the future by less than the usual time it takes the system to reboot, a crash won't force the clock sequence to get re-initialized. It's giving resilience ๐Ÿ‘‘

If it's too expensive to access shared state every time a UUID is generated, the system-wide generator can allocate a block of timestamps each time it's called. A per-process generator can then allocate from that block until it runs out ๐Ÿฆ‹


6.4. Distributed UUID Generation

Some implementations MAY desire multi-node, clustered applications, where two or more nodes independently generate UUIDs that get stored in a common location ๐ŸŒˆ UUIDs already have enough entropy that collisions are unlikely, but as the number of UUID generating nodes goes up, so does the chance of a collision ๐Ÿ˜ฌ

This section is going to walk through the two extra collision resistance approaches that multi-node UUID implementations in distributed environments have been seen using ๐Ÿ’…

Quick heads-up: even though this section covers two methods "for the sake of completeness," implementations should use the pseudorandom Node ID option if extra collision resistance for distributed UUID generation is a requirement ๐Ÿซถ Also, using either method isn't required to do UUID generation in distributed environments, so don't stress ๐ŸŒธ

Node IDs: With this method, a pseudorandom Node ID value gets placed inside the UUID layout โœจ That identifier helps make sure the bit space for a given node is unique, so the UUIDs won't clash with any UUID made by another node that has a different node id ๐Ÿงš Implementations that choose to use an embedded node id SHOULD use UUIDv8 ๐ŸŽ€ The node id SHOULD NOT be an IEEE 802 MAC address, per Section 8 ๐Ÿ™…โ€โ™€๏ธ Where the node id goes and how many bits it is are up to implementations and are outside the scope of this spec. Creating and negotiating unique node ids among nodes is also out of scope for this specification ๐Ÿ’œ

Centralized Registry: With this method, every node that creates UUIDs checks a central registry and confirms the generated value is unique ๐Ÿ“‹ But as apps scale up, talking to that central registry can turn into a bottleneck and hurt UUID generation ๐Ÿ˜ฉ Shared knowledge schemes with central or global registries are outside the scope of this specification and are NOT RECOMMENDED ๐Ÿ™…โ€โ™€๏ธ

Distributed applications generating UUIDs at a variety of hosts MUST be willing to rely on the random number source at all hosts ๐Ÿ”ฅ No backing out of that one, bestie ๐Ÿ’€


6.5. Name-Based UUID Generation

Some people like to call UUIDs made with hashing algorithms (MD5 or SHA-1) "hash-based," but this document keeps the term "name-based" so it stays consistent with earlier published documents and existing implementations ๐Ÿ“š Consistency is everything ๐Ÿ’…

The requirements for name-based UUIDs are:

  • UUIDs generated at different times from the same name (using the same canonical format) in the same namespace MUST be equal. ๐Ÿซถ
  • UUIDs generated from two different names (same or differing canonical format) in the same namespace should be different (with very high probability). โœจ
  • UUIDs generated from the same name (same or differing canonical format) in two different namespaces should be different (with very high probability). ๐Ÿฆ‹
  • If two UUIDs that were generated from names (using the same canonical format) are equal, then they were generated from the same name in the same namespace (with very high probability). ๐Ÿ‘‘

A note on names:

The idea of a "name" (and "namespace") should be broadly construed and not limited to text ๐ŸŒˆ A canonical sequence of octets is one that follows the specification for that name form's canonical representation. A name can take many common forms, but only one of them can be canonical ๐Ÿง If you're implementing new namespaces for UUIDs, you need to check the specification for the canonical form of names in that space, or define a canonical form for the namespace if it doesn't have one yet ๐Ÿ“

Here's the tea ๐Ÿต At the time of writing, the Domain Name System (DNS) [RFC9499] has three conveyance formats: common (www.example.com), presentation (www.example.com.), and wire format (3www7example3com0). Looking at [X500] Distinguished Names (DNs), [RFC4122] allowed either text-based or binary DER-based names as inputs. For Uniform Resource Locators (URLs) [RFC1738], you could give a Fully Qualified Domain Name (FQDN) with or without the protocol identifier, like www.example.com or https://www.example.com. When it comes to Object Identifiers (OIDs) [X660], you could pick dot notation without the leading dot (2.999), dot notation with the leading dot (.2.999), or one of the many formats from [X680], like OID Internationalized Resource Identifier (OID-IRI) (/Joint-ISO-ITU-T/Example) ๐Ÿคฏ

Most users will probably default to the common format for DNS, the FQDN format for a URL, the text format for X.500, and dot notation without a leading dot for OID. Even so, name-based UUID implementations generally SHOULD allow arbitrary input that computes name-based UUIDs for any of the example names above, and for others not defined here ๐ŸŽ€ Each name format within a namespace will output different UUIDs, so the mechanisms or conventions used for allocating names and making sure they're unique within their namespaces are beyond the scope of this specification ๐Ÿ™ˆ


6.6. Namespace ID Usage and Allocation

This section covers the namespace IDs for some potentially interesting namespaces, like DNS [RFC9499], URLs [RFC1738], OIDs [X660], and DNs [X500] ๐ŸŒธ

It also covers allocation, IANA registration, and other details that matter for Namespace IDs ๐Ÿ“‹

+=========+====================================+=========+==========+
|Namespace|Namespace ID Value                  |Name     |Namespace |
|         |                                    |Reference|ID        |
|         |                                    |         |Reference |
+=========+====================================+=========+==========+
|DNS      |6ba7b810-9dad-11d1-80b4-00c04fd430c8|[RFC9499]|[RFC4122],|
|         |                                    |         |RFC 9562  |
+---------+------------------------------------+---------+----------+
|URL      |6ba7b811-9dad-11d1-80b4-00c04fd430c8|[RFC1738]|[RFC4122],|
|         |                                    |         |RFC 9562  |
+---------+------------------------------------+---------+----------+
|OID      |6ba7b812-9dad-11d1-80b4-00c04fd430c8|[X660]   |[RFC4122],|
|         |                                    |         |RFC 9562  |
+---------+------------------------------------+---------+----------+
|X500     |6ba7b814-9dad-11d1-80b4-00c04fd430c8|[X500]   |[RFC4122],|
|         |                                    |         |RFC 9562  |
+---------+------------------------------------+---------+----------+

                           Table 3: Namespace IDs

Items can be added to this registry using the Specification Required policy, as per [RFC8126] ๐Ÿ“

For designated experts, Namespace IDs are generally allocated like this ๐Ÿง‘โ€๐Ÿ’ป

  • The first Namespace ID value, for DNS, was calculated from a time-based UUIDv1 and 6ba7b810-9dad-11d1-80b4-00c04fd430c8, used as a starting point.
  • Subsequent Namespace ID values increment the least significant, rightmost bit of time_low 6ba7b810 while freezing the rest of the UUID to 9dad-11d1-80b4-00c04fd430c8.
  • New Namespace ID values MUST use this same logic and MUST NOT use a previously used Namespace ID value. ๐Ÿšซ
  • Thus, 6ba7b815 is the next available time_low for a new Namespace ID value, with the full ID being 6ba7b815-9dad-11d1-80b4-00c04fd430c8. ๐Ÿฆ‹
  • The upper bound for time_low in this special use, Namespace ID values, is ffffffff or ffffffff-9dad-11d1-80b4-00c04fd430c8, which should be plenty of room for future Namespace ID values ๐ŸŒˆ
Heads up, bestie: the Namespace ID value 6ba7b813-9dad-11d1-80b4-00c04fd430c8 and its usage are not defined by this document or by [RFC4122]. So it SHOULD NOT be used as a Namespace ID value. ๐Ÿ™…โ€โ™€๏ธ

New Namespace ID values MUST be documented as per Section 7 if they're going to be globally available and fully interoperable ๐Ÿ“– Implementations MAY keep using vendor-specific, application-specific, and deployment-specific Namespace ID values, but just know that interoperability isn't guaranteed ๐Ÿซฃ These custom Namespace ID values MUST NOT use the logic above. Instead, generating a UUIDv4 or UUIDv7 Namespace ID value is RECOMMENDED ๐Ÿ’œ If collision probability (Section 6.7) and uniqueness (Section 6.8) of the final name-based UUID aren't a problem, an implementation MAY also use UUIDv8 instead to create a custom, application-specific Namespace ID value โœจ

Implementations SHOULD provide the ability to input a custom namespace, so they can account for newly registered IANA Namespace ID values outside of the ones listed in this section, or for custom, application-specific Namespace ID values ๐ŸŽ€

6.7. Collision Resistance ๐Ÿ’…

Okay so bestie, let's talk about collisions ๐Ÿ˜ญ Implementations should weigh what happens if two UUIDs end up the same inside their app, and that matters a LOT when they're deciding between UUID versions that lean on randomness (entropy) ๐ŸŽฒ versus the other bits, like the ones covered in Sections 6.1 and 6.2 โœจ. This is extra important for distributed node collision resistance, which is defined in Section 6.4 ๐Ÿซถ.

There are two example scenarios below to show how serious a collision can be depending on the app ๐Ÿ”ฅ.

Low Impact: A UUID collision made a duplicate log entry, and now the statistics built from that data are a little off ๐Ÿ“Š. Implementations that aren't hurt by collisions can keep going with the entropy and uniqueness that UUIDs from this document provide ๐ŸŒธ.

High Impact: A duplicate key sends an airplane the wrong course, and now people's lives are at risk ๐Ÿ˜ฑ๐Ÿ’€. In this scenario there is NO margin for error. Collisions must be avoided: failure is unacceptable. Apps handling this kind of thing must use as much collision resistance as they possibly can within their context ๐Ÿ‘‘.


6.8. Global and Local Uniqueness ๐ŸŒ

UUIDs made by this specification MAY be used to give local uniqueness guarantees ๐Ÿ . For example, making sure UUIDs created within a local app are unique inside a database MAY be enough for some implementations ๐Ÿ’–, especially when global uniqueness outside that app, in other apps, or around the world isn't needed ๐Ÿงš.

Okay, real talk: true global uniqueness is impossible to guarantee without a shared knowledge scheme ๐Ÿคฏ. But a UUID doesn't REQUIRE a shared knowledge scheme to be unique for practical implementation purposes โœจ. Implementations MAY use a shared knowledge scheme, introduced in Section 6.4, however they see fit to extend the uniqueness this specification guarantees ๐Ÿฆ‹.


6.9. Unguessability ๐Ÿ™ˆ

Implementations SHOULD use a cryptographically secure pseudorandom number generator (CSPRNG) ๐Ÿ” to produce values that are both hard to predict ("unguessable") and unlikely to collide ("unique") ๐Ÿ’œ. The exception is when a suitable CSPRNG isn't available in the execution environment ๐Ÿ™ƒ. Be careful to reseed the CSPRNG state properly whenever the state changes, like on process forks ๐Ÿด, so it keeps working correctly ๐Ÿ’…. The CSPRNG covers the best of Sections 6.7 and 8 in modern UUIDs ๐Ÿ‘‘.

If you want more advice on generating cryptographic-quality random numbers, check out [RFC4086], [RFC8937], and [RANDOM] ๐Ÿ“š๐ŸŒˆ.


6.10. UUIDs That Do Not Identify the Host ๐Ÿงšโ€โ™€๏ธ

This section explains how to generate a UUIDv1 or UUIDv6 value when an IEEE 802 address isn't available or isn't wanted ๐ŸŽ€.

Implementations MAY use MAC address randomization techniques [IEEE802.11bh] as an alternative to the pseudorandom logic in this section ๐Ÿฆ‹.

Or, implementations MAY choose to get a 48-bit cryptographic-quality random number, as per Section 6.9, and use it as the Node ID ๐ŸŽฒ. After generating the fully randomized 48-bit node value, implementations MUST set the least significant bit of the first octet of the Node ID to 1 โšก. This bit is the unicast or multicast bit, and it is never set in IEEE 802 addresses from network cards ๐Ÿ™Œ. So a UUID from a machine with a network card can NEVER conflict with one from a machine without one ๐Ÿ’–. There's an example of generating a randomized 48-bit node value and the bit modification in Appendix A ๐Ÿ“. For more on IEEE 802 addresses and the unicast or multicast and local/global bits, check out [RFC9542] ๐ŸŒธ.

Quick note for compatibility with earlier specs: this document uses the unicast or multicast bit instead of the arguably more correct local/global bit ๐Ÿค“. That's because MAC addresses with the local/global bit set or not set are BOTH possible on a network, but the unicast or multicast bit doesn't have that problem ๐Ÿ’…. One node can't have a MAC address that multicasts to multiple nodes ๐Ÿซถ.

Also, things like the computer's name and the operating system's name aren't strictly random, but they help tell results apart from the ones other systems make ๐Ÿ“.

How you build a Node ID from this data is system specific โœจ, since both the data available and the functions to get it are super system specific ๐Ÿ’ป. But a generic approach is to pile as many sources as you can into a buffer, run it through a message digest (like SHA-256 or SHA-512 from [FIPS180-4]) ๐Ÿ”ฅ, take an arbitrary 6 bytes from the hash value, and set the multicast bit like we talked about above ๐Ÿ’œ.


6.11. Sorting ๐Ÿ“Š

UUIDv6 and UUIDv7 are designed so that anything that needs sorting (like database indexes) can sort them as opaque raw bytes ๐Ÿ™Œ, without parsing or introspection ๐Ÿ’–.

Time-ordered monotonic UUIDs get better database-index locality because new values sit near each other in the index ๐Ÿฆ‹. So objects cluster together more easily, which means better performance ๐Ÿš€. In the real world, the difference between this index locality and random data inserts can be one order of magnitude or more ๐Ÿ˜ฑ๐Ÿ”ฅ.

UUID formats from this specification are meant to be lexicographically sortable in their textual representation ๐Ÿ“โœจ.

UUIDs made by this specification are crafted with big-endian byte order (network byte order) in mind ๐ŸŒ. If you need little-endian style, UUIDv8 is there for custom UUID formats ๐ŸŽ€.


6.12. Opacity ๐Ÿซฅ

As general advice, avoiding parsing UUID values unnecessarily is RECOMMENDED ๐Ÿ’…. Instead, treat UUIDs as opaquely as possible ๐Ÿ™ˆ. Sure, application-specific stuff could require some introspection (like examining Sections 4.1 or 4.2, or maybe the timestamp of a UUID) โฐ, but the advice here is to skip that or any other parsing unless it's absolutely necessary ๐Ÿ›‘. Apps tend to be simpler, more interoperable, and faster when you follow this advice ๐Ÿš€๐Ÿ’–.


6.13. DBMS and Database Considerations ๐Ÿ—„๏ธ

For a lot of apps, like databases, storing UUIDs as text is unnecessarily verbose ๐Ÿ˜ฉ. It takes 288 bits to represent a 128-bit UUID value as text! So where it's feasible, UUIDs SHOULD be stored in database apps as the underlying 128-bit binary value ๐Ÿ’œ.

For other systems, UUIDs MAY be stored in binary form or as text, whatever fits best ๐ŸŽ€. Here are the trade-offs:

  • Storing in binary form takes up less space and may result in faster data access ๐ŸŽ๏ธ.
  • Storing as text takes up more space, but may need less translation if the text form is used after retrieval, which may make it simpler to implement ๐Ÿ“.

DBMS vendors are ENCOURAGED to provide functionality to generate and store UUID formats from this specification ๐Ÿ‘‘. That's for use as identifiers or left parts of identifiers, like (but not limited to) primary keys ๐Ÿ”‘, surrogate keys for temporal databases โณ, foreign keys in polymorphic relationships ๐Ÿ”—, and keys for key-value pairs in JSON columns and key-value databases ๐Ÿงฉ. Apps using a monolithic database may find that database-generated UUIDs (instead of client-generated ones) give the BEST UUID monotonicity ๐Ÿ“ˆ. On top of UUIDs, additional identifiers MAY be used to ensure integrity and feedback ๐Ÿ›ก๏ธ.

Designers of database schemas are cautioned AGAINST using name-based UUIDs (see Sections 5.3 and 5.5) as primary keys in tables ๐Ÿšซ๐Ÿ˜ฌ. A super common mistake is assuming a particular value will never change, and then it totally does ๐Ÿ˜ญ. Postal codes, license numbers, and other identification numbers seem unique and unchanging at a given point in time, and then later they hit edge cases where they NEED to change ๐Ÿ™ƒ. When that identifier changes, and it was used as the "name" input for a name-based UUID, it can break a database structure ๐Ÿ’€. In those situations, using any non-name-based UUID version would have put the field somewhere easier to adapt to those changes (primary key excluded from this statement) ๐Ÿงธ. So the general advice is to avoid name-based UUID natural keys and use time-based UUID surrogate keys instead, for the problems described in this section ๐Ÿ’–.


7. IANA Considerations ๐ŸŒธ

All references to [RFC4122] in IANA registries (outside the ones created by this document) have been replaced with references to this document โœจ, including the IANA URN namespace registration [URNNamespaces] for UUID ๐ŸŒˆ. References to Section 4.1.2 of [RFC4122] have been updated to refer to Section 4 of this document ๐Ÿ“.

Finally, IANA should track UUID Subtypes and Special Case "Namespace IDs Values" as specified in Sections 7.1 and 7.2 at this location: <https://www.iana.org/assignments/uuid> ๐Ÿ”—๐Ÿ’œ.

When evaluating requests, the designated expert should consider community feedback ๐Ÿ—ฃ๏ธ, how well-defined the reference specification is ๐Ÿ“–, and this specification's requirements ๐Ÿ“‹. Vendor-specific, application-specific, and deployment-specific values CANNOT be registered ๐Ÿšซ. Specification documents should be published in a stable, freely available manner (ideally with a URL) ๐ŸŒ, but they don't need to be standards ๐Ÿ’…. The designated expert will either approve or deny the registration request and tell IANA the decision ๐Ÿ‘‘. Denials should include an explanation and, if it applies, suggestions for how to make the request work ๐Ÿซถ๐Ÿฆ‹.

7.1. IANA UUID Subtype Registry and Registration ๐Ÿ’…

okay so this section sets up a registry called "UUID Subtypes" for the common, widely used UUID standards ๐Ÿฆ‹ here's the whole table, bestie:

+======================+====+=========+================+============+
| Name                 | ID | Subtype | Variant        | Reference  |
+======================+====+=========+================+============+
| Gregorian Time-based | 1  | version | OSF DCE        | [RFC4122], |
|                      |    |         | / IETF         | RFC 9562   |
+----------------------+----+---------+----------------+------------+
| DCE Security         | 2  | version | OSF DCE        | [C309],    |
|                      |    |         | / IETF         | [C311]     |
+----------------------+----+---------+----------------+------------+
| MD5 Name-based       | 3  | version | OSF DCE        | [RFC4122], |
|                      |    |         | / IETF         | RFC 9562   |
+----------------------+----+---------+----------------+------------+
| Random               | 4  | version | OSF DCE        | [RFC4122], |
|                      |    |         | / IETF         | RFC 9562   |
+----------------------+----+---------+----------------+------------+
| SHA-1 Name-based     | 5  | version | OSF DCE        | [RFC4122], |
|                      |    |         | / IETF         | RFC 9562   |
+----------------------+----+---------+----------------+------------+
| Reordered Gregorian  | 6  | version | OSF DCE        | RFC 9562   |
| Time-based           |    |         | / IETF         |            |
+----------------------+----+---------+----------------+------------+
| Unix Time-based      | 7  | version | OSF DCE        | RFC 9562   |
|                      |    |         | / IETF         |            |
+----------------------+----+---------+----------------+------------+
| Custom               | 8  | version | OSF DCE        | RFC 9562   |
|                      |    |         | / IETF         |            |
+----------------------+----+---------+----------------+------------+

                        Table 4: IANA UUID Subtypes

This table may be extended by Standards Action as per [RFC8126] ๐ŸŒˆ

For designated experts, there are a few extra things to know โœจ

  • The minimum and maximum "ID" value for the subtype "version" within the "OSF DCE / IETF" variant is 0 through 15. The versions within Table 1 described as "Reserved for future definition" or "unused" are left out of this IANA registry until they're properly defined ๐Ÿ™ˆ
  • The "Subtype" column is free-form text. But at publication time, "version" and "family" are the only known UUID subtypes. The "family" subtype lives in the "Apollo NCS" variant space (both are outside the scope of this specification). The Microsoft variant may have subtyping mechanisms defined, but they're unknown and outside this specification's scope. Same deal with the final "Reserved for future definition" variant, which may bring new subtyping logic later on. Subtype IDs are permitted to overlap, so an ID of "1" can show up in multiple variant spaces ๐Ÿ”ฅ
  • The "Variant" column is also free-form text. It's likely that one of four values will show up: the first three are "OSF DCE / IETF", "Apollo NCS", and "Microsoft", and the final variant value belongs to the "Reserved for future definition" variant and may bring a new name in the future ๐Ÿ’œ

7.2. IANA UUID Namespace ID Registry and Registration โœจ

this specification defines the "UUID Namespace IDs" registry for common, widely used Namespace ID values ๐ŸŽ€

the full details of this registration, including the info for designated experts, are in Section 6.6 ๐Ÿ“

8. Security Considerations ๐Ÿ”’

okay bestie, this part is serious, so let's go ๐Ÿซถ

Implementations SHOULD NOT assume that UUIDs are hard to guess ๐Ÿ˜ญ For example, they MUST NOT be used as security capabilities (identifiers whose mere possession grants access). If a random number source turns out to be predictable, that's a vulnerability ๐Ÿ’€

Implementations MUST NOT assume it's easy to tell whether a UUID has been slightly modified to redirect a reference to another object. Humans can't easily check a UUID's integrity just by glancing at it ๐Ÿ™ˆ

MAC addresses come with inherent privacy risks, so they SHOULD NOT be used within a UUID. Instead, CSPRNG data SHOULD be selected from a source with sufficient entropy to guarantee uniqueness among UUID generation ๐Ÿ” See Sections 6.9 and 6.10 for more info ๐Ÿฆ‹

Timestamps embedded in a UUID do pose a very small attack surface ๐ŸŒธ The timestamp together with an embedded counter does signal the order of creation for a UUID and its corresponding data, but it doesn't tell you anything about the data itself or the application as a whole. If UUIDs are required for any security operation within an application context, in any shape or form, then UUIDv4 (Section 5.4) SHOULD be utilized ๐Ÿ’…

See [RFC6151] for MD5 security considerations and [RFC6194] for SHA-1 security considerations ๐ŸŒˆ

9. References ๐Ÿ“š

9.1. Normative References ๐Ÿงš

9.2. Informative References โœจ

okay bestie, these are the "for vibes and receipts" references ๐Ÿ’… Nothing here is strictly required to implement the spec, but they're where all the good stuff lives: real-world ID generators, errata, and the standards that the rest of the doc leans on ๐Ÿงš


Appendix A. Test Vectors ๐Ÿงช

okay so the test vectors are where we get to see the math in action, and it's honestly so satisfying ๐Ÿฅน Both the UUIDv1 and UUIDv6 vectors use the same 60-bit timestamp, which is 0x1EC9414C232AB00 (138648505420000000), and that's Tuesday, February 22, 2022 2:22:22.000000 PM GMT-05:00 ๐Ÿ•‘

Both UUIDv1 and UUIDv6 use the same values in clock_seq and node, and all of those were generated with random data ๐ŸŽฒ For the randomized node, the least significant bit of the first octet is set to a value of 1 as per Section 6.10. So the starting value 0x9E6BDECED846 got changed to 0x9F6BDECED846 ๐Ÿ’œ

The pseudocode for converting a 64-bit Unix timestamp into a 100 ns Gregorian timestamp value is still in the doc, just for reference, so you can follow the steps yourself ๐Ÿง 

# Gregorian-to-Unix Offset:
# The number of 100 ns intervals between the
# UUID Epoch 1582-10-15 00:00:00
# and the Unix Epoch 1970-01-01 00:00:00
# Greg_Unix_offset = 0x01b21dd213814000 or 122192928000000000

# Unix 64-bit Nanosecond Timestamp:
# Unix NS: Tuesday, February 22, 2022 2:22:22 PM GMT-05:00
# Unix_64_bit_ns = 0x16D6320C3D4DCC00 or 1645557742000000000

# Unix Nanosecond precision to Gregorian 100-nanosecond intervals
# Greg_100_ns = (Unix_64_bit_ns/100)+Greg_Unix_offset

# Work:
# Greg_100_ns = (1645557742000000000/100)+122192928000000000
# Unix_64_bit_ns = (138648505420000000-122192928000000000)*100

# Final:
# Greg_100_ns = 0x1EC9414C232AB00 or 138648505420000000

Figure 15: Test Vector Timestamp Pseudocode

A.1. Example of a UUIDv1 Value ๐Ÿฆ‹

Here's the full UUIDv1 example, field by field. It's giving clean breakdown ๐Ÿซถ

-------------------------------------------
field      bits value
-------------------------------------------
time_low   32   0xC232AB00
time_mid   16   0x9414
ver         4   0x1
time_high  12   0x1EC
var         2   0b10
clock_seq  14   0b11, 0x3C8
node       48   0x9F6BDECED846
-------------------------------------------
total      128
-------------------------------------------
final: C232AB00-9414-11EC-B3C8-9F6BDECED846

Figure 16: UUIDv1 Example Test Vector

A.2. Example of a UUIDv3 Value

okay so this one is the UUIDv3 example and it's giving MD5 realness ๐Ÿ’… we hash the DNS Namespace ID together with the Name "www.example.com" (that's Figure 17) โœจ then Figure 18 shows the field mapping and every value laid out bit by bit ๐Ÿฆ‹ and if you want to see the version and variant bit swaps up close, Figure 19 has you covered ๐ŸŽ€

Namespace (DNS):  6ba7b810-9dad-11d1-80b4-00c04fd430c8
Name:             www.example.com
------------------------------------------------------
MD5:              5df418813aed051548a72f4a814cf09e

                    Figure 17: UUIDv3 Example MD5
-------------------------------------------
field     bits value
-------------------------------------------
md5_high  48   0x5df418813aed
ver        4   0x3
md5_mid   12   0x515
var        2   0b10
md5_low   62   0b00, 0x8a72f4a814cf09e
-------------------------------------------
total     128
-------------------------------------------
final: 5df41881-3aed-3515-88a7-2f4a814cf09e

                Figure 18: UUIDv3 Example Test Vector
MD5 hex and dash:      5df41881-3aed-0515-48a7-2f4a814cf09e
Ver and Var Overwrite: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
Final:                 5df41881-3aed-3515-88a7-2f4a814cf09e

             Figure 19: UUIDv3 Example Ver/Var Bit Swaps

A.3. Example of a UUIDv4 Value

this UUIDv4 example is the random one ๐ŸŽฒโœจ the creator grabbed 16 bytes of random data and got the hex value 919108F752D133205BACF847DB4148A8 (so random, so iconic ๐Ÿคฉ) then those bytes get poured into the fields you see in Figure 20 ๐Ÿ’–

Then, to show off the version and variant bit swaps, check out Figure 21 ๐Ÿฆ‹

-------------------------------------------
field     bits value
-------------------------------------------
random_a  48   0x919108f752d1
ver        4   0x4
random_b  12   0x320
var        2   0b10
random_c  62   0b01, 0xbacf847db4148a8
-------------------------------------------
total     128
-------------------------------------------
final: 919108f7-52d1-4320-9bac-f847db4148a8

                Figure 20: UUIDv4 Example Test Vector
Random hex:            919108f752d133205bacf847db4148a8
Random hex and dash:   919108f7-52d1-3320-5bac-f847db4148a8
Ver and Var Overwrite: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
Final:                 919108f7-52d1-4320-9bac-f847db4148a8

             Figure 21: UUIDv4 Example Ver/Var Bit Swaps

A.4. Example of a UUIDv5 Value

this one is UUIDv5, the SHA-1 version ๐Ÿงฉ we use the DNS Namespace ID and the Name "www.example.com" for the computation in Figure 22 ๐Ÿ’… the field mapping and every value are in Figure 23 โœจ and then Figure 24 is the one that gets extra spicy, because it shows the version and variant bit swaps AND the leftover part of the SHA-1 value that gets thrown away ๐Ÿ™ˆ

Namespace (DNS):  6ba7b810-9dad-11d1-80b4-00c04fd430c8
Name:             www.example.com
----------------------------------------------------------
SHA-1:            2ed6657de927468b55e12665a8aea6a22dee3e35

                   Figure 22: UUIDv5 Example SHA-1
-------------------------------------------
field      bits value
-------------------------------------------
sha1_high  48   0x2ed6657de927
ver         4   0x5
sha1_mid   12   0x68b
var         2   0b10
sha1_low   62   0b01, 0x5e12665a8aea6a2
-------------------------------------------
total      128
-------------------------------------------
final: 2ed6657d-e927-568b-95e1-2665a8aea6a2

                Figure 23: UUIDv5 Example Test Vector
SHA-1 hex and dash:    2ed6657d-e927-468b-55e1-2665a8aea6a2-2dee3e35
Ver and Var Overwrite: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
Final:                 2ed6657d-e927-568b-95e1-2665a8aea6a2
Discarded:                                                 -2dee3e35

   Figure 24: UUIDv5 Example Ver/Var Bit Swaps and Discarded SHA-1
                               Segment

A.5. Example of a UUIDv6 Value

here's the UUIDv6 test vector ๐ŸŒธ the timestamp gets split up into its pieces, and the version and variant bits get slotted in right where they belong โœจ

-------------------------------------------
field       bits value
-------------------------------------------
time_high   32   0x1EC9414C
time_mid    16   0x232A
ver          4   0x6
time_high   12   0xB00
var          2   0b10
clock_seq   14   0b11, 0x3C8
node        48   0x9F6BDECED846
-------------------------------------------
total       128
-------------------------------------------
final: 1EC9414C-232A-6B00-B3C8-9F6BDECED846

                Figure 25: UUIDv6 Example Test Vector

A.6. Example of a UUIDv7 Value

this UUIDv7 test vector is giving clean timestamp energy ๐Ÿ•ฐ๏ธ๐Ÿ’œ it uses a well-known Unix Epoch timestamp with millisecond precision to fill the first 48 bits ๐Ÿงš

rand_a and rand_b get filled with random data ๐ŸŽฒ

the timestamp is Tuesday, February 22, 2022 2:22:22.00 PM GMT-05:00, which is 0x017F22E279B0 or 1645557742000 in the hex and decimal versions ๐Ÿ“

-------------------------------------------
field       bits value
-------------------------------------------
unix_ts_ms  48   0x017F22E279B0
ver          4   0x7
rand_a      12   0xCC3
var          2   0b10
rand_b      62   0b01, 0x8C4DC0C0C07398F
-------------------------------------------
total       128
-------------------------------------------
final: 017F22E2-79B0-7CC3-98C4-DC0C0C07398F

                Figure 26: UUIDv7 Example Test Vector

Appendix B. Illustrative Examples

these sections are all about how you can use UUIDv8 (Section 5.8) for custom and/or experimental, application-based logic ๐Ÿ› ๏ธโœจ quick heads-up though: these examples have NOT been through the same rigorous testing, prototyping, and feedback loop as the other algorithms in this document ๐Ÿ™ˆ the authors really encourage implementers to create their own UUIDv8 algorithm instead of just using the ones in this section ๐Ÿ’…

B.1. Example of a UUIDv8 Value (Time-Based)

this UUIDv8 test vector uses a well-known 64-bit Unix Epoch timestamp with 10 ns precision ๐Ÿ• it gets truncated to its least significant, rightmost bits to fill the first 60 bits of custom_a and custom_b, while the version bits sit between those two segments and get set to the version value of 8 ๐ŸŒˆ

the variant bits get set too, and the final segment, custom_c, is filled with random data ๐ŸŽฒ

the timestamp is Tuesday, February 22, 2022 2:22:22.000000 PM GMT-05:00, which is 0x2489E9AD2EE2E00 or 164555774200000000 (in 10 ns steps) โญ

-------------------------------------------
field     bits value
-------------------------------------------
custom_a  48   0x2489E9AD2EE2
ver        4   0x8
custom_b  12   0xE00
var        2   0b10
custom_c  62   0b00, 0xEC932D5F69181C0
-------------------------------------------
total     128
-------------------------------------------
final: 2489E9AD-2EE2-8E00-8EC9-32D5F69181C0

      Figure 27: UUIDv8 Example Time-Based Illustrative Example

B.2. Example of a UUIDv8 Value (Name-Based)

okay so per Section 5.5, name-based UUIDs that want to use modern hashing algorithms MUST be created within the UUIDv8 space ๐Ÿง  these MAY leverage newer hashing algorithms like SHA-256 or SHA-512 (as defined by [FIPS180-4]), SHA-3 or SHAKE (as defined by [FIPS202]), or even algorithms that haven't been defined yet ๐Ÿ”ฎ

the SHA-256 version of the SHA-1 computation from Appendix A.4 is in Figure 28, as an illustrative example of how this can be done ๐Ÿ’– the creation of the name-based UUIDv8 value here follows the exact same logic as Section 5.5, with SHA-256 swapped in for SHA-1 ๐Ÿฆ‹

the field mapping and all values are in Figure 29 โœจ and Figure 30 shows the version and variant bit swaps along with the unused/discarded part of the SHA-256 value ๐Ÿ™ˆ big important note: for secure hashing algorithms that produce outputs of an arbitrary size, such as the ones in SHAKE, the output hash MUST be 128 bits or larger ๐Ÿšจ

Namespace (DNS):       6ba7b810-9dad-11d1-80b4-00c04fd430c8
Name:                  www.example.com
----------------------------------------------------------------
SHA-256:
5c146b143c524afd938a375d0df1fbf6fe12a66b645f72f6158759387e51f3c8

                   Figure 28: UUIDv8 Example SHA256
-------------------------------------------
field     bits value
-------------------------------------------
custom_a  48   0x5c146b143c52
ver        4   0x8
custom_b  12   0xafd
var        2   0b10
custom_c  62   0b00, 0x38a375d0df1fbf6
-------------------------------------------
total     128
-------------------------------------------
final: 5c146b14-3c52-8afd-938a-375d0df1fbf6

  Figure 29: UUIDv8 Example Name-Based SHA-256 Illustrative Example
A: 5c146b14-3c52-4afd-938a-375d0df1fbf6-fe12a66b645f72f6158759387e51f3c8
B: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
C: 5c146b14-3c52-8afd-938a-375d0df1fbf6
D:                                     -fe12a66b645f72f6158759387e51f3c8

  Figure 30: UUIDv8 Example Ver/Var Bit Swaps and Discarded SHA-256
                               Segment

Examining Figure 30, with the lines broken down:

  • Line A details the full SHA-256 as a hexadecimal value with the dashes inserted. ๐Ÿ”
  • Line B details the version and variant hexadecimal positions, which must be overwritten. ๐Ÿซถ
  • Line C details the final value after the ver and var have been overwritten. ๐Ÿ‘‘
  • Line D details the discarded leftover values from the original SHA-256 computation. ๐Ÿ—‘๏ธ

Acknowledgements

the authors gratefully acknowledge the contributions of Rich Salz, Michael Mealling, Ben Campbell, Ben Ramsey, Fabio Lima, Gonzalo Salgueiro, Martin Thomson, Murray S. Kucherawy, Rick van Rein, Rob Wilton, Sean Leonard, Theodore Y. Ts'o, Robert Kieffer, Sergey Prokhorenko, and LiosK ๐Ÿซถ

as well as everyone in the IETF community and on GitHub who contributed to the discussions that led to this document ๐ŸŒˆ

this document draws heavily on the OSF DCE specification (Appendix A of [C309]) for UUIDs ๐Ÿ“š Ted Ts'o gave helpful comments ๐Ÿ’œ

we're also so grateful to the careful reading and bit-twiddling of Ralf S. Engelschall, John Larmouth, and Paul Thorpe ๐Ÿค“ Professor Larmouth was also invaluable in achieving coordination with ISO/IEC ๐Ÿ‘‘


Authors' Addresses

Kyzer R. Davis Cisco Systems Email: kydavis@cisco.com

Brad G. Peabody Uncloud Email: brad@peabody.io

Paul J. Leach University of Washington Email: pjl7@uw.edu

๐Ÿ“ฆ where this page comes from