Reference

OKID

OKE's native id generator — compact, URL-safe, cryptographically random ids from okengine/okid, with opt-in prefix, time-sortable, and alphabet-controlled variants.

Using okengine/okid gives you an id for any primary key, request trace, or event that is short (okid() is 21 characters), URL-safe, and random from a cryptographic source. Turn to it when a plain UUID string is more than you need; your app already generates them wherever defaultFn(id) is used.

The one rule

Use OKID for identity, never for secrets. An id is enumerable by design, so anything you hand to an untrusted client must be a token from the Vault, not an OKID.

Quick start

Install nothing — it is exported by the package

import { okid } from "okengine/okid";

Generate an id

const userId = okid();
const requestId = okid(16);
const typedId = okid({ prefix: "usr_" });
const eventKey = okid({ sortable: true });
const inviteCode = okid({ lookAlikes: false, uppercase: false });

Store it anywhere a string fits

// field.id() is shorthand for "default generation id" — currently OK ID.
field.id().primaryKey();
// Or pin OK ID explicitly:
field.okid().primaryKey();

The same 21-character id lands in your SQL primary keys, KV keys, and trace ids.

Reference

CallResultNotes
okid()21-char URL-safe id, 126 bits of entropy64-char alphabet, a-zA-Z0-9-_
okid(length)id of exactly length charactersinteger between 8 and 128
okid({ length })options form, body of lengthdefault 21
okid({ prefix })prefix + bodybody length unchanged; see Options
okid({ sortable })time-prefixed body, 8 + length − 8lexicographic order ≈ creation order
okid({ numbers, lowercase, uppercase, symbols })charset controleach group defaults to on
okid({ lookAlikes })confusable-char controllookAlikes: false drops 1lI0Oouv5Ss

Options

OptionTypeDefaultMeaning
lengthnumber21generated body length (8–128); does not include prefix
prefixstring""fixed label prepended to the body (e.g. "usr_", "evt_")
sortablebooleanfalseprefix the body with an 8-char epoch-ms timestamp
numbersbooleantrueinclude 0-9
lowercasebooleantrueinclude a-z
uppercasebooleantrueinclude A-Z
symbolsbooleantrueinclude - and _
lookAlikesbooleantrueinclude confusable chars 1lI0Oouv5Ss; set false to drop

Exported constants

ConstantValueMeaning
OKID_ALPHABETa-zA-Z0-9-_default, Base64URL order
OKID_SORTABLE_ALPHABETalphabet sorted by code unit (same chars)used by the sortable encoder
OKID_LOOKALIKE_CHARS1lI0Oouv5Ssdropped when lookAlikes: false
OKID_DEFAULT_LENGTH21default body length
OKID_MIN_LENGTH8shortest non-sortable body
OKID_MAX_LENGTH128longest body
OKID_SORTABLE_MIN_LENGTH16shortest sortable body (8+8)
OKID_MAX_PREFIX_LENGTH32longest semantic prefix

Collision resistance

Every character is drawn uniformly from the alphabet with crypto.getRandomValues(). Because it uses an unbiased character selection (never modulo), each character carries exactly log2(alphabet) bits of entropy. At the default 21 characters over 64 symbols, that is 126 bits — the birthday-bound collision probability across one billion ids is on the order of 10⁻²¹. You do not need a UUID for collision resistance; this is where a UUID is stronger only because it is a different format, not a different amount of randomness.

Consequence: two ids minted at the same millisecond are still distinct — the timestamp prefix never replaces entropy, it prefixes it.

Semantic prefixes

prefix is a fixed label ("usr_", "evt_", "inst-") prepended to the generated body. Characters must belong to OKID_ALPHABET (max 32). length stays the body size; the returned string is prefix + body.

Consequence: okid({ prefix: "usr_", sortable: true }) yields usr_ + 8-char timestamp + random tail — the label sorts first, then time.

Sortable ids

sortable: true prepends 48 bits of Date.now() encoded in exactly 8 characters, in an alphabet whose sort order matches time order. Sorting a batch of these ids reproduces the creation order across milliseconds.

Consequence: a sortable id embeds its creation time (millisecond precision), so keep them out of public, enumerable surfaces. Clock skew distorts order but can never produce a duplicate — the tail stays random.

Alphabet control

Turning groups off shrinks the alphabet. With a non-power-of-two alphabet, OKID uses rejection sampling instead of modulo, so every remaining character stays equally likely — the output never becomes measurably biased.

Consequence: smaller alphabets mean fewer bits per character. lookAlikes: false alone drops the default entropy only slightly (126 → ~120 bits); dropping whole groups costs more. Choose the smallest alphabet that fits the human-transcription use case.

Under the hood

The generator is a pure function: no counters, no process or machine fingerprint, no shared mutable state. It is safe to call concurrently from any number of workers, and every body uses only the bytes it needs — no hidden timestamp unless sortable is on.

Troubleshooting

Learn more

  • StoredefaultFn(id) in table declarations delegates to okid()
  • fxfx.id() returns an OKID; options live here
  • Clock — process instanceId (inst-<okid>) is an OKID

Next

On this page