Skip to main content

core.id — unique identifiers

Generators for identifiers that must not collide.

They lived in core/base/ until the stdlib's ring law measured the consequence: base is ring 0, the language's own vocabulary, and these modules mount core.time.system_time (ring 2). core.base.snowflake was claiming that a Snowflake ID is a primitive of the LANGUAGE, alongside Maybe and Result. It is not — it is a timestamp, a machine id and a sequence number.

moduleshape
core.id.uuidRFC 9562 UUIDs
core.id.ulidlexicographically sortable, timestamp-prefixed (Crockford base32)
core.id.nanoidshort, URL-safe, entropy only — 21 chars, 126 bits
core.id.snowflake64-bit: timestamp + machine + sequence

An identifier is not a secret​

All four draw from core.random.secure, so the entropy is sound. That is a property of the GENERATOR, not of the format: ulid and snowflake embed a timestamp by design and reveal their creation time to anyone holding one. If you need a value an observer cannot correlate, nanoid carries no structure — or draw the bytes yourself.

None of these is a hash. For content addressing see core.hash.crypto; for a keyed tag see core.mac.

Per-module status​

filestatusdetail
uuid.vrpartialcore-tests/base/uuid — Uuid.parse and Uuid.from_bytes are stable.
ulid.vrpartialcore-tests/base/ulid — unit, property, integration and regression suites green under the interpreter. The data surface is fully covered: the from_parts and from_parts_seeded factories, parse validation, the Crockford alphabet, the two-variant UlidError, and the 48-bit timestamp round trip. Known limitation: Ulid.new() does not run — it panics before producing a value, reporting an unresolved stub for a uniquely-named public function. Build a ULID from explicit parts with from_parts until it lands; the data surface above is unaffected. The generating entry points Ulid.now and generate share new's path.
nanoid.vrpartialcore-tests/base/nanoid — the conformance suite is green under the interpreter. 16 unit + 14 property + 9 integration + 12 regression pins. Pure-data surface — NANOID_ALPHABET (64-char URL-safe alphabet: A-Z + a-z + 0-9 + _ + -), NANOID_DEFAULT_LENGTH = 21, alphabet composition, URL-safety, 126-bit-entropy invariant — fully covered. Three @ignore'd pins for two cascading stdlib defects (both fixed in this branch, activate on next precompiled-stdlib refresh): (a) §A Text.from("") undefined — core/base/nanoid.vr:138 called a non-existent Text.from factory; fix: → Text.new(). (b) §B Text.from_utf8_unchecked private — same fix as ulid §A (text.vr:455 unsafe fn → public unsafe fn). Combined effect: every code path through generate_with_alphabet (including generate() and generate_len) lenient-stubbed at precompile, panics at runtime. Workaround: constant + alphabet-invariant surface exclusively.
snowflake.vrpartialcore-tests/base/snowflake — unit, property, integration and regression suites green under the interpreter. The data surface is fully covered: Snowflake.new validation, the layout constants, the SnowflakeError variants and their equality, the parse-then-build identity, SnowflakeParts construction and the accessors.