moonuuid

    RFC 9562 UUID library for MoonBit with v3-v8, monotonic v7, secure generation, and binary/text interop

    uuid
    uuidv7
    rfc9562
    identifier
    moonbit
    Download zip
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    10 hours ago
    Downloads
    3

    Dependencies

    #MoonUUID

    CI

    RFC 9562 UUID infrastructure for MoonBit. UUIDv3 / v4 / v5 / v6 / v7 / v8, monotonic UUIDv7, secure platform entropy, deterministic provider injection, binary/text interoperability, and multi-backend CI.

    MoonUUID is designed as a reusable ecosystem library rather than an application-specific framework. Typical consumers are Web services, database layers, event systems, CLIs, storage adapters and distributed applications that need a stable UUID primitive.

    • Standard: RFC 9562
    • Module: wulisususu/moonuuid
    • Version: 0.1.0
    • License: Apache-2.0
    • Targets: wasm, wasm-gc, js, native

    #Why MoonUUID?

    MoonBit already has UUID implementations; MoonUUID does not claim an empty ecosystem. Its independent scope is RFC 9562-oriented infrastructure with secure default generation and operational UUIDv7 semantics: UUIDv6 support, injectable clock/entropy providers, no weak-random fallback, same-millisecond monotonicity, clock-rollback handling and explicit overflow behavior.

    See Differentiation for a documented comparison with existing MoonBit UUID packages and Compatibility for backend/runtime behavior.

    #Install

    After the first Mooncakes release:

    moon add wulisususu/moonuuid@0.1.0

    Import the root package:

    import {
    "wulisususu/moonuuid" @uuid
    }

    #Quick start

    Generate a time-ordered UUIDv7:

    match @uuid.v7() {
    Ok(id) => println(@uuid.to_string(id))
    Err(_) => println("secure entropy unavailable")
    }

    Generate deterministic resource IDs:

    let id = @uuid.v5_string(
    @uuid.namespace_url(),
    "https://example.com/users/42",
    )
    println(@uuid.to_string(id))

    Parse transport/storage forms without weakening the strict parser:

    let strict = @uuid.parse(
    "017f22e2-79b0-7cc3-98c4-dc0c0c07398f",
    )

    let interoperable = @uuid.parse_permissive(
    "urn:uuid:017f22e2-79b0-7cc3-98c4-dc0c0c07398f",
    )

    For strict generation-order monotonicity inside one process:

    let generator = @uuid.V7Generator::new()
    let next_id = generator.next()

    #What is implemented

    CapabilityStatusMain API
    Canonical parse/format✅parse, to_string
    Compact / URN / braced text✅parse_permissive, to_urn, to_braced_string
    16-byte network-order interop✅to_bytes, from_bytes
    UUIDv3✅v3, v3_string
    UUIDv4✅v4, v4_with_entropy, v4_from_entropy
    UUIDv5✅v5, v5_string
    UUIDv6✅v6_from_parts
    UUIDv7✅v7, v7_with, v7_from_parts
    Monotonic UUIDv7✅V7Generator
    UUIDv8 custom fields✅v8_from_parts
    SHA-256 UUIDv8 profile✅v8_sha256, v8_sha256_string
    Standard namespaces✅DNS / URL / OID / X.500
    Nil / Max / version / variant✅inspection helpers
    RFC vectors✅v3 / v4 / v5 / v6 / v7 / v8
    Cross-target CI✅wasm / wasm-gc / js / Linux native / Windows native

    #Realistic integration examples

    Runnable examples live under examples/ and are retained in the Mooncakes publication archive:

    • Web request ID — generate UUIDv7 for an X-Request-ID style correlation identifier.
    • Database key — generate a monotonic sequence of UUIDv7 values for index-friendly ordered keys.
    • Deterministic resource ID — derive the same UUIDv5 from the same namespace and logical resource name.
    • Wire interoperability — accept a UUID URN and round-trip it through the 16-byte representation.

    Run them with:

    moon run examples/web_request_id --target native moon run examples/database_key --target native moon run examples/deterministic_resource_id --target native moon run examples/interop --target native

    #UUIDv7 safety model

    v7() uses MoonBit's wall clock and secure platform entropy. It does not silently substitute Math.random, a timestamp-only value or another weak PRNG.

    V7Generator additionally handles:

    • multiple IDs in the same millisecond;
    • clock rollback;
    • rand_b carry into rand_a;
    • explicit overflow instead of knowingly producing duplicate/non-monotonic output.

    Provider-injected APIs keep the bit-layout logic deterministic and testable.

    #Portability

    CI checks the same public library across:

    • WebAssembly;
    • WebAssembly GC;
    • JavaScript;
    • Linux native;
    • Windows native.

    Secure entropy availability is runtime-dependent. If a target cannot provide secure entropy, random generation returns an explicit error while deterministic parsing, formatting, name-based UUIDs and field constructors continue to work.

    #Benchmarks

    MoonUUID includes release-mode microbenchmarks for canonical parsing, canonical formatting, UUIDv4 construction, UUIDv5 derivation and UUIDv7 construction.

    moon bench benchmarks --release --target native --deny-warn

    See docs/BENCHMARKS.md for methodology and the recorded CI baseline.

    #Documentation

    #Verification

    The repository CI runs:

    moon fmt --check moon check --target <wasm|wasm-gc|js|native> --deny-warn moon test --target <wasm|wasm-gc|js|native> moon build --target <wasm|wasm-gc|js|native> moon info --target native moon package --list moon bench benchmarks --release --target native --deny-warn

    Windows native is checked and tested separately.

    #Scope

    MoonUUID is intentionally a UUID foundation library. It is not an ORM, database, tracing framework, distributed-ID service or workflow engine.

    That boundary keeps the package useful to all of those higher-level systems without coupling it to any one of them.

    #Release status

    0.1.0 is the first release candidate. Packaging metadata, archive filtering, examples, API documentation, benchmarks and the release checklist are in-repository before the first Mooncakes publication. The release-readiness workflow verifies the package file list and uploads the generated Mooncakes candidate ZIP as a CI artifact. A separate guarded publish workflow performs the authenticated Mooncakes release, then verifies a clean external registry install before creating GitHub Release v0.1.0.

    #License

    Apache-2.0

    BytesError

    pub(all) enum BytesError {
    InvalidByteLength(Int)
    } derive(Eq)

    Error returned while converting binary UUID data.

    BytesError::equal

    fn BytesError::equal(BytesError, BytesError) -> Bool

    BytesError::not_equal

    fn BytesError::not_equal(x : BytesError, y : BytesError) -> Bool

    GenerateError

    pub(all) enum GenerateError {
    EntropyUnavailable
    InvalidEntropyLength(Int)
    } derive(Eq)

    Errors that can occur while generating a UUID.

    GenerateError::equal

    GenerateError::not_equal

    fn GenerateError::not_equal(x : GenerateError, y : GenerateError) -> Bool

    ParseError

    pub(all) enum ParseError {
    InvalidLength(Int)
    InvalidHyphen(Int)
    InvalidHexDigit(Int)
    InvalidWrapper
    } derive(Eq)

    Error returned while parsing UUID text.

    ParseError::equal

    fn ParseError::equal(ParseError, ParseError) -> Bool

    ParseError::not_equal

    fn ParseError::not_equal(x : ParseError, y : ParseError) -> Bool

    Uuid

    pub struct Uuid {
    high : UInt64
    low : UInt64
    } derive(Compare, Eq, Hash)

    A UUID represented as two unsigned 64-bit words in network byte order.

    high stores octets 0..7 and low stores octets 8..15.

    Uuid::compare

    fn Uuid::compare(Uuid, Uuid) -> Int

    Uuid::equal

    fn Uuid::equal(Uuid, Uuid) -> Bool

    Uuid::hash

    fn Uuid::hash(self : Uuid) -> Int

    Uuid::hash_combine

    fn Uuid::hash_combine(Uuid, Hasher) -> Unit

    Uuid::not_equal

    fn Uuid::not_equal(x : Uuid, y : Uuid) -> Bool

    Uuid::op_ge

    fn Uuid::op_ge(x : Uuid, y : Uuid) -> Bool

    Uuid::op_gt

    fn Uuid::op_gt(x : Uuid, y : Uuid) -> Bool

    Uuid::op_le

    fn Uuid::op_le(x : Uuid, y : Uuid) -> Bool

    Uuid::op_lt

    fn Uuid::op_lt(x : Uuid, y : Uuid) -> Bool

    V6Error

    pub(all) enum V6Error {
    TimestampOutOfRange(UInt64)
    ClockSeqOutOfRange(UInt64)
    NodeOutOfRange(UInt64)
    } derive(Eq)

    Errors returned while constructing UUIDv6 values.

    V6Error::equal

    fn V6Error::equal(V6Error, V6Error) -> Bool

    V6Error::not_equal

    fn V6Error::not_equal(x : V6Error, y : V6Error) -> Bool

    V7Error

    pub(all) enum V7Error {
    EntropyUnavailable
    InvalidEntropyLength(Int)
    TimestampOutOfRange(UInt64)
    RandAOutOfRange(UInt64)
    RandBOutOfRange(UInt64)
    MonotonicOverflow
    } derive(Eq)

    Errors that can occur while constructing or generating UUIDv7 values.

    V7Error::equal

    fn V7Error::equal(V7Error, V7Error) -> Bool

    V7Error::not_equal

    fn V7Error::not_equal(x : V7Error, y : V7Error) -> Bool

    V7Generator

    pub struct V7Generator {
    initialized : Bool
    last_ms : UInt64
    rand_a : UInt64
    rand_b : UInt64
    }

    Stateful UUIDv7 generator for strict monotonic ordering on one node/process.

    For a new millisecond, fresh random fields are generated. For the same millisecond, or when the supplied clock moves backwards, the previous timestamp is reused and the 74-bit random payload is incremented.

    V7Generator::new

    Construct an empty monotonic UUIDv7 generator.

    V7Generator::next

    fn V7Generator::next(self : V7Generator) -> Result[Uuid, V7Error]

    Generate the next strictly monotonic UUIDv7 using MoonBit platform providers.

    V7Generator::next_with

    fn V7Generator::next_with(self : V7Generator, clock : () -> UInt64, entropy : (Int) -> Bytes?) -> Result[Uuid, V7Error]

    Generate the next strictly monotonic UUIDv7 with injected providers.

    RFC 9562 section 6.2 guidance is followed:
    • a newer millisecond reseeds the random fields;
    • the same millisecond increments the prior random payload;
    • clock rollback reuses the previous timestamp and increments the payload;
    • full 74-bit payload rollover returns MonotonicOverflow.

    V8Error

    pub(all) enum V8Error {
    CustomAOutOfRange(UInt64)
    CustomBOutOfRange(UInt64)
    CustomCOutOfRange(UInt64)
    } derive(Eq)

    Errors returned while constructing UUIDv8 values.

    V8Error::equal

    fn V8Error::equal(V8Error, V8Error) -> Bool

    V8Error::not_equal

    fn V8Error::not_equal(x : V8Error, y : V8Error) -> Bool

    Variant

    pub(all) enum Variant {
    Ncs
    Rfc9562
    Microsoft
    Future
    } derive(Eq)

    UUID layout variant as defined by RFC 9562 section 4.1.

    Variant::equal

    fn Variant::equal(Variant, Variant) -> Bool

    Variant::not_equal

    fn Variant::not_equal(x : Variant, y : Variant) -> Bool

    from_bytes

    fn from_bytes(data : Bytes) -> Result[Uuid, BytesError]

    Decode exactly 16 bytes in RFC network byte order into a UUID.

    from_u64s

    fn from_u64s(high : UInt64, low : UInt64) -> Uuid

    Construct a UUID directly from its high and low 64-bit words.

    gregorian_ts_100ns

    fn gregorian_ts_100ns(uuid : Uuid) -> UInt64?

    Extract the 60-bit Gregorian 100-ns timestamp from an RFC UUIDv6.

    Returns None for non-v6 UUIDs.

    high

    fn high(uuid : Uuid) -> UInt64

    Return the most-significant 64 bits.

    is_max

    fn is_max(uuid : Uuid) -> Bool

    Return true when this is the RFC 9562 Max UUID.

    is_nil

    fn is_nil(uuid : Uuid) -> Bool

    Return true when this is the RFC 9562 Nil UUID.

    is_valid

    fn is_valid(text : String) -> Bool

    Return true if text is a canonical UUID string accepted by parse.

    is_valid_permissive

    fn is_valid_permissive(text : String) -> Bool

    Return true if text is accepted by parse_permissive.

    low

    fn low(uuid : Uuid) -> UInt64

    Return the least-significant 64 bits.

    max

    fn max() -> Uuid

    RFC 9562 Max UUID: all 128 bits are one.

    namespace_dns

    fn namespace_dns() -> Uuid

    RFC 9562 / RFC 4122 DNS namespace UUID.

    namespace_oid

    fn namespace_oid() -> Uuid

    RFC 9562 / RFC 4122 OID namespace UUID.

    namespace_url

    fn namespace_url() -> Uuid

    RFC 9562 / RFC 4122 URL namespace UUID.

    namespace_x500

    fn namespace_x500() -> Uuid

    RFC 9562 / RFC 4122 X.500 DN namespace UUID.

    nil

    fn nil() -> Uuid

    RFC 9562 Nil UUID: all 128 bits are zero.

    parse

    fn parse(text : String) -> Result[Uuid, ParseError]

    Parse only the canonical UUID text form: 8-4-4-4-12.

    Both uppercase and lowercase hexadecimal digits are accepted. Other common wrappers and compact text are intentionally rejected here. Use parse_permissive when interoperability with those forms is required.

    parse_permissive

    fn parse_permissive(text : String) -> Result[Uuid, ParseError]

    Parse common interoperable UUID text forms.

    Accepted forms:
    • canonical: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    • compact: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    • URN: urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    • braced: {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}

    to_braced_string

    fn to_braced_string(uuid : Uuid) -> String

    Format a UUID using the common braced canonical form.

    to_bytes

    fn to_bytes(uuid : Uuid) -> Bytes

    Encode the UUID as exactly 16 bytes in RFC network byte order.

    to_string

    fn to_string(uuid : Uuid) -> String

    Format a UUID as lowercase canonical 8-4-4-4-12 text.

    to_urn

    fn to_urn(uuid : Uuid) -> String

    Format a UUID as an RFC UUID URN.

    unix_ts_ms

    fn unix_ts_ms(uuid : Uuid) -> UInt64?

    Return the 48-bit Unix-millisecond timestamp embedded in a UUIDv7.

    Returns None for UUIDs that are not RFC-variant version 7 values.
    fn v3(ns : Uuid, name : Bytes) -> Uuid

    Generate an RFC 9562 UUIDv3 from a namespace UUID and arbitrary name bytes.

    UUIDv3 uses MD5 for compatibility with the standardized name-based format. This API does not treat MD5 as a general-purpose security primitive.

    v3_string

    fn v3_string(ns : Uuid, name : String) -> Uuid

    Generate UUIDv3 from a UTF-8 encoded string name.

    Name canonicalization is namespace/application specific and remains the caller's responsibility.
    fn v4() -> Result[Uuid, GenerateError]

    Generate an RFC 9562 UUIDv4 using MoonBit's platform entropy source.

    @env.rand uses a secure platform entropy source when available. This function returns EntropyUnavailable instead of falling back to a weak PRNG.

    Current MoonBit target behavior includes:
    • native: runtime platform entropy
    • js: globalThis.crypto.getRandomValues
    • wasm: WASI random_get when the host provides it
    • wasm-gc: currently unavailable in the MoonBit core runtime

    v4_from_entropy

    fn v4_from_entropy(entropy : Bytes) -> Result[Uuid, GenerateError]

    Apply the RFC 9562 UUIDv4 version and variant bits to exactly 16 entropy bytes.

    This function is deterministic and does not obtain randomness itself. It is useful for tests, protocol adapters, and runtimes that already own a secure entropy source.

    v4_with_entropy

    fn v4_with_entropy(provider : (Int) -> Bytes?) -> Result[Uuid, GenerateError]

    Generate a UUIDv4 using an injected entropy provider.

    The provider is asked for exactly 16 secure random bytes. Returning None reports EntropyUnavailable; returning a byte string of another length reports InvalidEntropyLength.
    fn v5(ns : Uuid, name : Bytes) -> Uuid

    Generate an RFC 9562 UUIDv5 from a namespace UUID and arbitrary name bytes.

    UUIDv5 uses SHA-1 because that algorithm is part of the standardized UUIDv5 format. This API should not be interpreted as recommending SHA-1 for new cryptographic designs.

    v5_string

    fn v5_string(ns : Uuid, name : String) -> Uuid

    Generate UUIDv5 from a UTF-8 encoded string name.

    Name canonicalization is namespace/application specific and remains the caller's responsibility.

    v6_clock_seq

    fn v6_clock_seq(uuid : Uuid) -> UInt64?

    Extract the 14-bit clock sequence from an RFC UUIDv6.

    Returns None for non-v6 UUIDs.

    v6_from_parts

    fn v6_from_parts(timestamp_100ns : UInt64, clock_seq : UInt64, node : UInt64) -> Result[Uuid, V6Error]

    Construct an RFC 9562 UUIDv6 from UUIDv1-compatible fields.

    timestamp_100ns is the 60-bit count of 100 ns intervals since 1582-10-15 00:00:00 UTC. clock_seq is 14 bits and node is 48 bits.

    v6_node

    fn v6_node(uuid : Uuid) -> UInt64?

    Extract the 48-bit node field from an RFC UUIDv6.

    Returns None for non-v6 UUIDs.
    fn v7() -> Result[Uuid, V7Error]

    Generate UUIDv7 using MoonBit's wall clock and secure platform entropy.

    Returns EntropyUnavailable when the runtime cannot supply secure randomness.

    v7_from_entropy

    fn v7_from_entropy(unix_ms : UInt64, entropy : Bytes) -> Result[Uuid, V7Error]

    Construct UUIDv7 from a Unix-millisecond timestamp and 10 entropy bytes.

    The first 12 usable bits seed rand_a; the following 62 usable bits seed rand_b. Excess high bits are masked away to preserve the RFC layout.

    v7_from_parts

    fn v7_from_parts(unix_ms : UInt64, rand_a : UInt64, rand_b : UInt64) -> Result[Uuid, V7Error]

    Construct UUIDv7 from its RFC 9562 fields.

    unix_ms must fit in 48 bits, rand_a in 12 bits and rand_b in 62 bits.

    v7_with

    fn v7_with(clock : () -> UInt64, entropy : (Int) -> Bytes?) -> Result[Uuid, V7Error]

    Generate UUIDv7 using injected clock and entropy providers.

    The clock returns Unix time in milliseconds. The entropy provider is asked for exactly 10 bytes.

    v8_custom_a

    fn v8_custom_a(uuid : Uuid) -> UInt64?

    Extract UUIDv8 custom_a (48 bits), or None for non-v8 UUIDs.

    v8_custom_b

    fn v8_custom_b(uuid : Uuid) -> UInt64?

    Extract UUIDv8 custom_b (12 bits), or None for non-v8 UUIDs.

    v8_custom_c

    fn v8_custom_c(uuid : Uuid) -> UInt64?

    Extract UUIDv8 custom_c (62 bits), or None for non-v8 UUIDs.

    v8_from_parts

    fn v8_from_parts(custom_a : UInt64, custom_b : UInt64, custom_c : UInt64) -> Result[Uuid, V8Error]

    Construct an RFC 9562 UUIDv8 from application-defined fields.

    UUIDv8 reserves 48 bits as custom_a, 12 bits as custom_b, and 62 bits as custom_c; MoonUUID inserts the required version and variant.

    v8_sha256

    fn v8_sha256(ns : Uuid, name : Bytes) -> Uuid

    Generate the RFC 9562 Appendix B.2 SHA-256 name-based UUIDv8 profile.

    RFC 9562 defines UUIDv8 as application-specific. This helper follows the document's illustrative SHA-256 construction: hash namespace bytes followed by name bytes, take the first 128 hash bits, then overwrite version/variant.

    v8_sha256_string

    fn v8_sha256_string(ns : Uuid, name : String) -> Uuid

    UTF-8 string convenience wrapper for the illustrative SHA-256 UUIDv8 profile.

    variant

    fn variant(uuid : Uuid) -> Variant

    Return the UUID layout variant from the most-significant bits of octet 8.

    version

    fn version(uuid : Uuid) -> Int?

    Return the 4-bit UUID version for RFC 9562-layout UUIDs.

    Non-RFC variants do not share the RFC version-field semantics and return None.