Skip to content

nomasystems/nuid

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nuid

Hex.pm CI

Unique identifier generation for Erlang/OTP 27+: RFC 4122 and RFC 9562 UUIDs, plus sortable nuid identifiers.

Getting started

%% rebar.config
{deps, [nuid]}.
1> nuid:uuid4().
<<"37a9e737-f680-44a9-b83d-a517ec758b75">>
2> nuid:uuid7().
<<"018b3d7a-9f9a-7577-adb2-08761e3d87f7">>
3> nuid:nuid2().
<<"OHtpP-----Fkn3F6JaT5Kxnm_NAiDzFgGMzc">>
4> nuid:uuid7_info(nuid:uuid7()).
{{2023,10,17},{11,52,8}}

Features

  • RFC 4122 UUIDs versions 1, 3 (MD5), 4, and 5 (SHA-1) (RFC 4122)
  • RFC 9562 UUIDs versions 6, 7, and 8 (RFC 9562)
  • Nil and max UUIDs
  • nuid1 and nuid2 sortable identifiers with 128 bits of cryptographically strong randomness
  • Info recovery creation time and originating node from generated identifiers
  • No dependencies only OTP kernel, stdlib, and crypto

API

Function Description
nuid:uuid1/0 RFC 4122 UUID v1
nuid:uuid3/2 RFC 4122 UUID v3 (MD5, name-based)
nuid:uuid4/0 RFC 4122 UUID v4 (random)
nuid:uuid5/2 RFC 4122 UUID v5 (SHA-1, name-based)
nuid:uuid6/0 RFC 9562 UUID v6 (time-ordered)
nuid:uuid7/0 RFC 9562 UUID v7 (time-ordered)
nuid:uuid8/1, nuid:uuid8/3 RFC 9562 UUID v8 (vendor-specific)
nuid:nil_uuid/0 RFC 4122 nil UUID
nuid:max_uuid/0 RFC 9562 max UUID
nuid:nuid1/0 nuid1 identifier
nuid:nuid2/0 nuid2 identifier
nuid:uuid1_info/1, nuid:uuid6_info/1 Generation time, node, and counter
nuid:uuid7_info/1, nuid:nuid1_info/1 Generation time
nuid:nuid2_info/1 Generation time and node

The nuid identifiers

We have historically used UUID v1 (RFC 4122) and later UUID v6 (RFC 9562), preferring v6 because it can be ordered by creation time.

A later security review required identifiers with at least as many cryptographically strong random bits as UUID v4 (122). The requirements were:

  • Orderable by creation time.
  • At least 122 bits of cryptographically strong randomness.

With, optionally:

  • Uniqueness (or at least a low collision probability).
  • Information about where the identifier was created.

The two de facto standards we looked at, ulid and ksuid, do not meet the randomness requirement, so we defined nuid2.

nuid2 properties:

  • Lexicographically sortable.
  • Sixteen cryptographically strong random bytes (128 bits).
  • Unique (or at least a low collision probability).
  • Carries 3 bytes reserved for origin information.
  • No longer than a UUID (36 characters).
  • URL-safe.
 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    POSIX time in seconds                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    Unique sortable integer                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        Origin information (node)                |             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+             +
|                                                               |
+                                                               +
|          Cryptographically strong random bits (128)           |
+                                                               +
|                                                               |
+                                                 +-+-+-+-+-+-+-+
|                                                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

              Figure 1: nuid2 field and bit layout

We encode this in what we call base64': a URL-safe, sortable base64 representation. Its alphabet, in order:

-, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
_,
a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z

It is the Erlang/OTP base64 module reordered to preserve byte ordering.

nuid1 is a lighter identifier with these properties:

  • Lexicographically sortable. It can replace UUID v6: every nuid1 generated after a UUID v6 sorts after it.
  • Sixteen cryptographically strong random bytes (128 bits).
  • Unique (or at least a low collision probability).
 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 2 3 4 5 6
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    Hex unique time in us  |-|       base64' 16 random bytes             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

              Figure 2: nuid1 field and byte layout

(*) the dash at byte 14 is ASCII 45

Benchmarks

Run a rebar3 shell using the bench profile:

rebar3 as bench shell
1> nuid_bench:bench().

This benchmark compares the different identifiers generated by nuid.

nuid1 creation time: 1.83 (us)
nuid2 creation time: 1.55 (us)
uuid1 creation time: 0.92 (us)
uuid4 creation time: 1.69 (us)
uuid6 creation time: 0.91 (us)
uuid7 creation time: 1.82 (us)

Documentation

nuid on HexDocs

License

Apache License 2.0

About

Unique identifier generation for Erlang/OTP 27+: RFC 4122 and RFC 9562 UUIDs, plus sortable `nuid` identifiers.

Resources

License

Stars

Watchers

Forks

Contributors

Languages