(l)exigraphical (U)TC and (r)andomness (id)entifier = lurid
  • TypeScript 100%
Find a file
2026-06-11 15:59:12 -07:00
tests fix: remove unused import in test 2026-06-11 15:58:29 -07:00
.gitignore feature: lurids are lexographical UTC and randomness identifiers 2025-06-04 22:49:59 -07:00
cli.ts feature: 1.0 2026-06-11 15:54:36 -07:00
deno.json feature: 1.0 2026-06-11 15:54:36 -07:00
lurid.ts feature: 1.0 2026-06-11 15:54:36 -07:00
README.md docs: one more touch 2026-06-11 15:59:12 -07:00
word_bytes.ts feature: 1.0 2026-06-11 15:54:36 -07:00

lurid

(l)exigraphical (U)TC and (r)andomness (id)entifier = lurid

lexicographically sortable, human-readable ids.

comparison

format human readable? lexicographically sortable?
uuid no no
ksuid no yes
lurid yes yes

uuids are not sortable. ksuids are but you cannot easily say them aloud. lurids are both — every byte maps to a common short word so anyone can read one back, and time is encoded in the first seven words so they sort naturally.

format

a lurid is 10 words separated by dashes, for example:

able-flat-give-call-spot-type-deck-soft-need-last

each word is a byte mapped to one of 256 common four-letter words. 7 bytes of timestamp + 3 bytes of randomness from crypto.getRandomValues().

roughly, they're:

time-time-time-time-time-time-time-rand-rand-rand

examples

generate a handful of lurids back-to-back:

[machine:~/] for run in {1..10}; do lurid; done
able-flat-give-burn-star-more-ring-land-cone-over
able-flat-give-burn-star-near-inch-coat-wand-name
able-flat-give-burn-star-noon-wind-flow-left-gone
able-flat-give-burn-star-over-both-slow-gold-trot
able-flat-give-burn-star-path-made-room-food-have
able-flat-give-burn-star-plod-your-long-soft-that
able-flat-give-burn-star-push-iron-fact-glad-land
able-flat-give-burn-star-real-ring-mark-tidy-tort
able-flat-give-burn-star-road-burn-book-wind-push
able-flat-give-burn-star-rope-gold-limo-neck-page

note they're sorted (lexicographically ordered).

let's parse one:

[machine:~/] lurid able-flat-give-burn-star-more-ring-land-cone-over
{
  valid: true,
  input: "able-flat-give-burn-star-more-ring-land-cone-over",
  words: [
    "able", "flat",
    "give", "burn",
    "star", "more",
    "ring", "land",
    "cone", "over"
  ],
  time_words: [
    "able", "flat",
    "give", "burn",
    "star", "more",
    "ring"
  ],
  time_values: [
      0,  63,  72, 20,
    191, 129, 160
  ],
  time_as_int: 17812177482260480,
  time_as_double: 1781217748226.048,
  time_string: "2026-06-11T22:42:28.226Z",
  randomness_words: [ "land", "cone", "over" ],
  randomness_bytes: [ 104, 30, 142 ],
  randomness_value: 9313896
}

use --time-only to just get the utc date string:

[machine:~/] lurid --time-only able-flat-give-burn-star-more-ring-land-cone-over
2026-06-11T22:42:28.226Z

cli usage

install and run:

deno install -gA jsr:@andyburke/lurid/cli
lurid
able-flat-give-burn-with-wife-push-fade-crop-mean

options:

  -h, --help        show this help message
  -v, --version     show the version number
  -b, --basis       set the basis value for time (unix epoch by default)
  -s, --separator   set the word separator (default: -)
  -t, --time-only   show only the extracted utc date string when parsing

api

import { default: lurid, parse, get_utc_string } from 'jsr:@andyburke/lurid';

const id = lurid(); // 'able-flat-give-busy-blow-with-room-real-cold-keep'
const parsed = parse( id );
const utc = get_utc_string( id ); // '2026-06-11T22:44:38.197Z'

// custom time basis (unix epoch offset) and separator
const id_with_different_basis = lurid( 778946684800, '_' ); // 'able_cove_rail_race_stem_huge_ball_soft_wish_mark'

parse returns:

{
  valid: boolean,
  input: string,
  words: string[],         // all 10 words
  time_words: string[],    // first 7 words
  time_values: number[],   // byte values of time words
  time_as_int: number,     // timestamp as integer (ms * 10000)
  time_as_double: number,  // timestamp as ms since basis
  time_string: string,     // ISO 8601 UTC date
  randomness_words: string[], // last 3 words
  randomness_bytes: number[],   // byte values of randomness words
  randomness_value: number      // combined randomness int
}

word bytes (dictionary)

the dictionary is exported separately for your own encoding needs:

import { convert_to_words, convert_from_words, LURID_WORD_BYTE_DICTIONARY } from '@jsr:@andyburke/lurid/word_bytes';

const words = convert_to_words( [ 0, 62, 35 ] );   // ['able', 'fish', 'cove']
const bytes = convert_from_words( words );         // [ 0, 62, 35 ]

console.dir( {
	LURID_WORD_BYTE_DICTIONARY,
	words,
	bytes
} );

license

MIT