https://github.com/pmarreck/uuidv7
RFC 9562 UUIDv7 generator in LuaJIT: time-ordered, sortable, monotonic counter
https://github.com/pmarreck/uuidv7
Last synced: 3 days ago
JSON representation
RFC 9562 UUIDv7 generator in LuaJIT: time-ordered, sortable, monotonic counter
- Host: GitHub
- URL: https://github.com/pmarreck/uuidv7
- Owner: pmarreck
- License: mit
- Created: 2026-06-01T23:10:00.000Z (17 days ago)
- Default Branch: yolo
- Last Pushed: 2026-06-01T23:24:43.000Z (17 days ago)
- Last Synced: 2026-06-02T01:14:33.532Z (17 days ago)
- Language: Shell
- Size: 14.6 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# uuidv7
A small, fast [UUIDv7](https://www.rfc-editor.org/rfc/rfc9562) generator written in
[LuaJIT](https://luajit.org/). UUIDv7 values are **time-ordered**, so they sort
chronologically and make excellent database primary keys (far better index locality
than random UUIDv4).
RFC 9562 compliant: 48-bit millisecond Unix timestamp, version/variant bits, and a
12-bit monotonic counter so that multiple UUIDs minted within the same millisecond
remain strictly ordered and unique.
## Features
- **Strictly monotonic & time-ordered** — lexicographic order == chronological order,
with no reliance on randomness for ordering
- **True nanosecond precision on Linux *and* macOS** — `clock_gettime(CLOCK_REALTIME)`
(macOS ≥ 10.12), not microsecond `gettimeofday`
- **Sub-millisecond ordering for free** — the low 20 bits of the nanosecond-within-ms go
into `rand_a` + the top of `rand_b` (RFC 9562 Method 3), giving ~1 ns ordering
resolution; per-millisecond monotonicity comes from the clock, not a counter
- **54-bit monotonic counter** in the rest of `rand_b` — random-initialized each
nanosecond tick, incremented only on a same-nanosecond collision (so in practice every
UUID still carries ~53 bits of fresh entropy). Cross-process via System V shared memory
(Linux) or a `flock`'d file (macOS), both under `$TMPDIR`
- **Clock-rollback safe** — freeze-and-increment keeps monotonicity regardless of how far
the wall clock jumps back (the 54-bit counter can't overflow in any real window)
- **Hyphenated or compact** output; **zero heavy deps** — just LuaJIT
- **Reversible timestamps** — recover the exact epoch time encoded in a UUIDv7 (this
tool's nanosecond precision included) with `--extract-timestamp[-ms|-ns]`, independent
of the counter/random bits
### Behavior & environment
- `uuidv7 ` uses an explicit timestamp (honored *exactly* — 64-bit-parsed,
not rounded through a double), useful for testing/reproducibility.
- `TMPDIR` — where the cross-process counter state lives (default `/tmp`).
- `UUIDV7_SILENCE_INSECURE_RANDOM=1` — mute the red stderr warning that fires if no secure
RNG (`getrandom`/`getentropy`/`/dev/urandom`) is available and it must fall back to the
non-cryptographic `math.random`.
- `UUIDV7_NS_EXTRACT_OK=1` (or `--mute-warning`) — mute the stderr warning that
`--extract-timestamp-ns` prints, reminding that sub-millisecond bits are only
meaningful for UUIDs generated by this tool.
## Usage
```sh
uuidv7 # 019e8570aa20718baaf95dbc246169f9
uuidv7 --hyphen # 019e8570-aa29-7367-9482-75b33fab32b6
uuidv7 - # short form for --hyphen
uuidv7 1234567890123456789 # use a static timestamp (nanoseconds from epoch)
# Recover the original timestamp from a UUIDv7 (hyphenated or compact, any case):
uuidv7 --extract-timestamp-ms 017f22e2-79b0-77ff-bfc9-0c9a45e78a47 # 1645557742000
uuidv7 --extract-timestamp-ns 017f22e279b077ffbfc90c9a45e78a47 # 1645557742000524287
uuidv7 --extract-timestamp 017f22e279b077ffbfc90c9a45e78a47 # defaults to ms
```
Run `uuidv7 --help` for the full list.
## Daemon
`uuidv7 --daemon` runs a single-threaded, `poll(2)`-based **Unix-domain-socket** server
(`${XDG_RUNTIME_DIR:-$TMPDIR}/uuidv7.sock`, mode `0600`). Because it's one process, the
counter is in-memory and strictly monotonic with no cross-process coordination at all.
The plain `uuidv7` CLI **transparently uses the daemon** for live generation when its
socket is reachable (no auto-spawn). When a daemon *is* reachable it is authoritative: the
CLI gets the UUID from it and **never silently generates locally** — local generation uses
the separate fs/SysV counter, so a silent fallback could emit a UUID that sorts out of
order against the daemon's in-memory stream, breaking strict monotonicity. If the daemon
momentarily won't serve, the CLI retries with tiny exponential backoff (50 µs base,
~1.5× with jitter), tagging each retry with `R`/`W<µs-waited>` tokens the daemon
logs, then **errors** once the attempt cap (`UUIDV7_DAEMON_RETRIES`, default 20) or the
wait timeout (`UUIDV7_DAEMON_RETRY_TIMEOUT_US`, default 2000) is hit. `UUIDV7_DAEMON_NO_RETRY=1`
errors on the first failure instead of retrying. Local generation happens only when **no**
daemon answers the socket (the normal standalone mode) — that's not a fallback.
It's a line-based protocol (newline-terminated, no spaces), so it's `nc -U`-friendly:
```
one compact UUID c N UUIDs
- hyphenated tn explicit nanosecond time
tm explicit millisecond time * ephemeral (with tn/tm): isolated
R retry attempt # (telemetry) W microseconds waited (telemetry)
example: c5-tm1700000000000
PING -> OK s -> status (text) sj -> status (JSON) l -> log stats
r -> restart (re-exec, zero-downtime) +++ -> shutdown h or ? -> help
handshake (no newline): SYN(0x16) -> SYNACK(0x16 0x06) -> ACK(0x06)
```
Guards: batch capped at `UUIDV7_MAX_BATCH` (default 1000); request lines ≤ 64 bytes;
explicit timestamps more than 1 s in the future are rejected unless `*` (ephemeral) is
given. Activity (start / restart / shutdown / stats / errors) is logged as **JSONL** —
one JSON object per line, every byte escaped, so the log is `jq`-friendly and
injection-proof. Log location: `$XDG_STATE_HOME/uuidv7/uuidv7.log`, else
`~/Library/Logs/uuidv7.log` on macOS, else `~/.local/state/uuidv7/uuidv7.log`.
## Performance
Single-threaded, in-process generation sustains **~490,000 unhyphenated UUIDv7/s
(~2.0 µs each)**. It's syscall-bound, not compute-bound: every UUID does a
`clock_gettime` for the timestamp and — on each new-nanosecond tick — a
`getrandom`/`getentropy` to random-seed the 54-bit counter, so ~2 syscalls/UUID is the
floor. The benchmark also verifies its output is strictly increasing (unique + sorted)
and valid (`ver=7`, `variant=10xx`).
Measured on an **Apple M4 Max** (12P+4E, 128 GB), macOS 26.5, LuaJIT 2.1. Reproduce:
```sh
bench/uuidv7_bench # default 2,000,000
bench/uuidv7_bench 1000000 # custom count
```
(The daemon adds UDS round-trip overhead per request, but batching — `c` — amortizes
it, so a single `c1000000` request produces a million at close to the in-process rate.)
## Install
### Nix (flake)
```sh
nix run github:pmarreck/uuidv7
nix profile install github:pmarreck/uuidv7
```
### Manual
Put `bin/` on your `PATH`. Requires `luajit` on your `PATH`.
## Development
```sh
direnv allow # or: nix develop
./test # run the suite
nix flake check # hermetic CI check (also what Garnix runs)
```
`./test` runs eight suites: the JSON writer, the core generator, the RFC bit-layout
contract, the daemon black-box test (which starts a real daemon and drives it — no
sleeps; it synchronizes on the daemon's readiness line and on stdout EOF), timestamp
extraction, the daemon retry / no-silent-fallback policy, a **load test** (many parallel
clients; gates on correctness invariants only — zero duplicates, strictly-ascending
per-connection streams, client↔server count reconciliation, no crashes — never on
timing), and a **fuzz test** (hostile/malformed/partial protocol input; seeded and
reproducible). All eight also run under `nix flake check` (and Garnix, including
x86_64-linux). Raw throughput/latency lives in `bench/uuidv7_bench`, kept out of the
pass/fail suites so loaded CI never makes them flaky.
## Layout
```
bin/uuidv7 thin CLI (also `--daemon`); auto-uses a running daemon
lib/uuidv7_core.lua generation core: bit layout + counter backends (single source of truth)
lib/uuidv7_daemon.lua the UDS daemon (poll loop, protocol, stats, JSONL log, re-exec)
lib/uuidv7_client.lua UDS client (CLI fast-path + test driver)
lib/uuidv7_json.lua minimal, escape-everything JSON encoder
tests/uuidv7_{json,test,layout,daemon,extract,fallback,load,fuzz}_test the eight CI suites
tests/uuidv7_sysv_counter_test Linux-only SysV test (reference; not in CI)
alternates/uuidv7.{bash,sh} pure-bash / POSIX-sh references (not installed)
flake.nix dev shell, packaged + wrapped binary, and CI check
test test runner
```
The `alternates/` implementations are earlier takes kept for lineage/comparison. They
are not installed.
## License
MIT © Peter Marreck