moonloom

    MoonLoom is a self-describing content-addressing toolkit for MoonBit.

    multiformats
    multibase
    multicodec
    multihash
    cid
    multiaddr
    Download zip
    Version
    0.1.1
    License
    Apache-2.0
    Last updated
    9 hours ago
    Downloads
    2

    Dependencies

    #MoonLoom

    MoonLoom is a pure MoonBit toolkit for self-describing content addressing. It brings Multibase, Multicodec, Multihash, CID, and Multiaddr into one small, strict, deterministic library.

    MoonLoom is not a full IPFS implementation. It focuses on the format layer: creating canonical identifiers, converting between text and bytes, validating input, and preserving interoperability with the Multiformats ecosystem.

    #Features

    • Canonical unsigned varint encoding and strict decoding.
    • Multibase for base16, base32, base58btc, base64, and URL-safe base64.
    • One registry for CID, hash, content format, and Multiaddr protocol metadata.
    • Multihash with identity, sha2-256, and sha2-512.
    • CIDv0 and CIDv1 creation, parsing, canonical text, and verification.
    • Typed Multiaddr model, strict text parser, binary codec, and RFC 5952 IPv6 canonical text.
    • Reusable bounded WireReader and WireWriter primitives.
    • Typed errors, byte offsets, and configurable resource limits.
    • Multibase, Multihash, CID, and Multiaddr CLI commands.
    • wasm, wasm-gc, JS, and Native target checking.

    #Install

    moon add ggbond44439/moonloom@0.1.1

    In another MoonBit package:

    import { "ggbond44439/moonloom" @moonloom, }

    #Quick start

    let text = @moonloom.multibase_encode(@moonloom.Base32, b"hello")
    assert_eq(text, Ok("bnbswy3dp"))

    let provider = @moonloom.sha2_provider()
    let cid = match @moonloom.cid_from_content(
    @moonloom.RAW_CODEC,
    @moonloom.Sha2_256,
    b"hello",
    provider,
    @moonloom.Limits::default(),
    ) {
    Ok(value) => value
    Err(err) => fail(err.to_string())
    }

    assert_eq(
    cid.to_text(),
    Ok("bafkreibm6jg3ux5qumhcn2b3flc3tyu6dmlb4xa7u5bf44yegnrjhc4yeq"),
    )
    assert_eq(
    cid.verify(b"hello", provider, @moonloom.Limits::default()),
    Ok(()),
    )

    #CLI

    moonloom --help moonloom version moonloom multibase encode [--base <name>] <text> moonloom multibase decode <multibase-text> moonloom multihash digest [--algorithm sha2-256|sha2-512] <text> moonloom multihash inspect <multibase-multihash> moonloom multihash verify <multibase-multihash> <text> moonloom cid encode [--version 0|1] [--codec <name>] [--algorithm <name>] <text> moonloom cid decode <cid> moonloom cid verify <cid> <text> moonloom multiaddr parse <text> moonloom multiaddr encode <text> moonloom multiaddr decode [--format base64|hex] <data>

    Run the local CLI with:

    moon run cmd/main -- version moon run cmd/main -- cid encode hello

    #Examples

    moon run examples/artifact_cid moon run examples/multiaddr_normalize moon run examples/cross_language_fixture

    #Standards

    • RFC 4648 base encodings
    • Multiformats Multibase, Multicodec, Multihash, CID, and Multiaddr
    • RFC 5952 IPv6 canonical text
    • SHA-2 through the Apache-2.0 MoonCrypt package

    #Build and test

    moon check --target all moon test --target all moon build moon package --list

    #Status

    MoonLoom 0.1.1 is the format-core release for the 2026 September MoonBit Hackathon. The library, CLI, examples, interoperability vectors, and CI matrix are complete for the scoped formats.

    #License

    Apache-2.0.

    Base

    pub(all) enum Base {
    Base16
    Base16Upper
    Base32
    Base32Upper
    Base32Pad
    Base32PadUpper
    Base58Btc
    Base64
    Base64Pad
    Base64Url
    Base64UrlPad
    } derive(Eq,
    Debug
    )

    Multibase encodings supported by MoonLoom's first milestone.

    Base::equal

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

    Base::from_name

    fn Base::from_name(name : StringView) -> Base?

    Base::from_prefix

    fn Base::from_prefix(prefix : Char) -> Base?

    Base::name

    fn Base::name(self : Base) -> String

    Base::not_equal

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

    Base::prefix

    fn Base::prefix(self : Base) -> Char

    Base::to_repr

    Cid

    pub(all) struct Cid {
    version : UInt64
    codec : UInt64
    hash : Multihash
    } derive(Eq,
    Debug
    )

    CIDv0 or CIDv1 value.

    Cid::codec_name

    fn Cid::codec_name(self : Cid) -> String?

    Cid::decode_bytes

    fn Cid::decode_bytes(input : BytesView, limits : Limits) -> Result[Cid, MoonLoomError]

    Cid::equal

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

    Cid::hash_algorithm

    fn Cid::hash_algorithm(self : Cid) -> HashAlgorithm?

    Cid::is_v0

    fn Cid::is_v0(self : Cid) -> Bool

    Cid::not_equal

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

    Cid::parse

    fn Cid::parse(text : StringView, limits : Limits) -> Result[Cid, MoonLoomError]

    Cid::to_bytes

    fn Cid::to_bytes(self : Cid) -> Bytes

    Cid::to_repr

    Cid::to_text

    fn Cid::to_text(self : Cid) -> Result[String, MoonLoomError]

    Cid::v0

    fn Cid::v0(hash : Multihash, limits : Limits) -> Result[Cid, MoonLoomError]

    Cid::v1

    fn Cid::v1(codec : UInt64, hash : Multihash, limits : Limits) -> Result[Cid, MoonLoomError]

    Cid::verify

    fn Cid::verify(self : Cid, content : BytesView, provider : HashProvider, limits : Limits) -> Result[Unit, MoonLoomError]

    CodecCategory

    pub(all) enum CodecCategory {
    Cid
    Format
    Hash
    Multiaddr
    } derive(Eq,
    Debug
    )

    High-level namespace for a multicodec entry.

    CodecCategory::equal

    CodecCategory::from_name

    fn CodecCategory::from_name(name : StringView) -> CodecCategory?

    CodecCategory::name

    fn CodecCategory::name(self : CodecCategory) -> String

    CodecCategory::not_equal

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

    CodecInfo

    pub(all) struct CodecInfo {
    code : UInt64
    name : String
    category : CodecCategory
    } derive(Eq,
    Debug
    )

    One curated entry from the multicodec registry.

    CodecInfo::equal

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

    CodecInfo::not_equal

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

    DecodedMultibase

    pub(all) struct DecodedMultibase {
    base : Base
    data : Bytes
    } derive(Eq,
    Debug
    )

    Result of decoding a Multibase string.

    DecodedMultibase::equal

    DecodedMultibase::not_equal

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

    HashAlgorithm

    pub(all) enum HashAlgorithm {
    Identity
    Sha2_256
    Sha2_512
    } derive(Eq,
    Debug
    )

    Hash algorithms currently understood by MoonLoom.

    HashAlgorithm::code

    fn HashAlgorithm::code(self : HashAlgorithm) -> UInt64

    HashAlgorithm::equal

    HashAlgorithm::expected_digest_size

    fn HashAlgorithm::expected_digest_size(self : HashAlgorithm) -> Int?

    HashAlgorithm::from_code

    fn HashAlgorithm::from_code(code : UInt64) -> HashAlgorithm?

    HashAlgorithm::from_name

    fn HashAlgorithm::from_name(name : StringView) -> HashAlgorithm?

    HashAlgorithm::name

    fn HashAlgorithm::name(self : HashAlgorithm) -> String

    HashAlgorithm::not_equal

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

    HashProvider

    pub struct HashProvider {
    digest : (HashAlgorithm, BytesView) -> Result[Bytes, MoonLoomError]
    }

    Provider seam for cryptographic hash implementations.

    HashProvider::hash

    fn HashProvider::hash(self : HashProvider, algorithm : HashAlgorithm, data : BytesView) -> Result[Bytes, MoonLoomError]

    HashProvider::new

    fn HashProvider::new(digest : (HashAlgorithm, BytesView) -> Result[Bytes, MoonLoomError]) -> HashProvider

    Ipv4Address

    pub struct Ipv4Address {
    octets : Bytes
    } derive(Eq,
    Debug
    )

    Ipv4Address::equal

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

    Ipv4Address::from_octets

    fn Ipv4Address::from_octets(value : BytesView) -> Result[Ipv4Address, MoonLoomError]

    Ipv4Address::new

    fn Ipv4Address::new(a : Int, b : Int, c : Int, d : Int) -> Result[Ipv4Address, MoonLoomError]

    Ipv4Address::not_equal

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

    Ipv4Address::octets

    fn Ipv4Address::octets(self : Ipv4Address) -> BytesView

    Ipv4Address::to_text

    fn Ipv4Address::to_text(self : Ipv4Address) -> String

    Ipv6Address

    pub struct Ipv6Address {
    octets : Bytes
    } derive(Eq,
    Debug
    )

    Ipv6Address::equal

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

    Ipv6Address::from_octets

    fn Ipv6Address::from_octets(value : BytesView) -> Result[Ipv6Address, MoonLoomError]

    Ipv6Address::not_equal

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

    Ipv6Address::octets

    fn Ipv6Address::octets(self : Ipv6Address) -> BytesView

    Ipv6Address::to_text

    fn Ipv6Address::to_text(self : Ipv6Address) -> String

    Limits

    pub struct Limits {
    max_input_bytes : Int
    max_varint_bytes : Int
    max_digest_bytes : Int
    max_multiaddr_bytes : Int
    }

    Resource limits shared by all formats.

    Limits::check_digest

    fn Limits::check_digest(self : Limits, context : String, length : Int) -> Result[Unit, MoonLoomError]

    Validate a digest length before allocation or decoding.

    Limits::check_input

    fn Limits::check_input(self : Limits, context : String, length : Int) -> Result[Unit, MoonLoomError]

    Limits::check_multiaddr

    fn Limits::check_multiaddr(self : Limits, length : Int) -> Result[Unit, MoonLoomError]

    Validate a Multiaddr byte length before allocation or decoding.

    Limits::default

    fn Limits::default() -> Limits

    Limits::max_digest_bytes

    fn Limits::max_digest_bytes(self : Limits) -> Int

    Limits::max_input_bytes

    fn Limits::max_input_bytes(self : Limits) -> Int

    Limits::max_multiaddr_bytes

    fn Limits::max_multiaddr_bytes(self : Limits) -> Int

    Limits::max_varint_bytes

    fn Limits::max_varint_bytes(self : Limits) -> Int

    Limits::new

    fn Limits::new(max_input_bytes? : Int, max_varint_bytes? : Int, max_digest_bytes? : Int, max_multiaddr_bytes? : Int) -> Limits

    MoonLoomError

    pub(all) enum MoonLoomError {
    InputTooLong(String, Int, Int)
    OffsetOutOfBounds(Int)
    Truncated(String, Int)
    InvalidVarint(String, Int)
    NonCanonicalVarint(Int)
    UnknownBase(Char)
    InvalidBaseCharacter(Char, Int)
    InvalidPadding(String, Int)
    UnknownCodec(UInt64)
    UnknownCodecName(String)
    InvalidField(String, Int)
    UnsupportedHashAlgorithm(String)
    DigestLengthMismatch(String, Int, Int)
    HashMismatch
    InvalidMultihash(String, Int)
    InvalidCid(String, Int)
    CidCodecMismatch(UInt64)
    InvalidCodec(UInt64)
    InvalidProtocolValue(String)
    InvalidPort(Int)
    UnknownProtocol(String)
    } derive(Eq,
    Debug
    )

    Errors returned by MoonLoom parsing and encoding APIs.

    Lengths and offsets are measured in bytes. String contexts identify the format boundary that rejected the input.

    MoonLoomError::equal

    MoonLoomError::not_equal

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

    MoonLoomError::output

    fn MoonLoomError::output(self : MoonLoomError, logger : &Logger) -> Unit

    MoonLoomError::to_string

    fn MoonLoomError::to_string(self : MoonLoomError) -> String

    Multiaddr

    pub struct Multiaddr {
    segments : Array[PathSegment]
    } derive(Eq,
    Debug
    )

    Type-safe Multiaddr domain model. Text and binary codecs are layered on top.

    Multiaddr::empty

    fn Multiaddr::empty() -> Multiaddr

    Multiaddr::equal

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

    Multiaddr::from_segments

    fn Multiaddr::from_segments(segments : Array[PathSegment], limits : Limits) -> Result[Multiaddr, MoonLoomError]

    Multiaddr::get

    fn Multiaddr::get(self : Multiaddr, index : Int) -> PathSegment?

    Multiaddr::is_empty

    fn Multiaddr::is_empty(self : Multiaddr) -> Bool

    Multiaddr::length

    fn Multiaddr::length(self : Multiaddr) -> Int

    Multiaddr::not_equal

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

    Multiaddr::parse

    fn Multiaddr::parse(text : StringView, limits : Limits) -> Result[Multiaddr, MoonLoomError]

    Multiaddr::parse_bytes

    fn Multiaddr::parse_bytes(input : BytesView, limits : Limits) -> Result[Multiaddr, MoonLoomError]

    Multiaddr::push

    fn Multiaddr::push(self : Multiaddr, segment : PathSegment, limits : Limits) -> Result[Multiaddr, MoonLoomError]

    Multiaddr::segments

    fn Multiaddr::segments(self : Multiaddr) -> ArrayView[PathSegment]

    Multiaddr::to_bytes

    fn Multiaddr::to_bytes(self : Multiaddr, limits : Limits) -> Result[Bytes, MoonLoomError]

    Multiaddr::to_text

    fn Multiaddr::to_text(self : Multiaddr) -> Result[String, MoonLoomError]

    Multihash

    pub(all) struct Multihash {
    code : UInt64
    digest : Bytes
    } derive(Eq,
    Debug
    )

    Multihash::algorithm

    fn Multihash::algorithm(self : Multihash) -> HashAlgorithm?

    Multihash::create

    fn Multihash::create(algorithm : HashAlgorithm, digest : BytesView, limits : Limits) -> Result[Multihash, MoonLoomError]

    Multihash::decode

    fn Multihash::decode(input : BytesView, limits : Limits) -> Result[(Multihash, Int), MoonLoomError]

    Multihash::encode

    fn Multihash::encode(self : Multihash) -> Bytes

    Multihash::equal

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

    Multihash::from_text

    fn Multihash::from_text(text : StringView, limits : Limits) -> Result[Multihash, MoonLoomError]

    Multihash::identity

    fn Multihash::identity(data : BytesView, limits : Limits) -> Result[Multihash, MoonLoomError]

    Multihash::is_identity

    fn Multihash::is_identity(self : Multihash) -> Bool

    Multihash::not_equal

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

    Multihash::to_text

    fn Multihash::to_text(self : Multihash, base : Base) -> Result[String, MoonLoomError]

    Multihash::to_text_canonical

    fn Multihash::to_text_canonical(self : Multihash) -> Result[String, MoonLoomError]

    Multihash::verify

    fn Multihash::verify(self : Multihash, content : BytesView, provider : HashProvider, limits : Limits) -> Result[Unit, MoonLoomError]

    PathSegment

    pub struct PathSegment {
    protocol : Protocol
    value : ProtocolValue
    } derive(Eq,
    Debug
    )

    A typed protocol/value pair.

    PathSegment::equal

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

    PathSegment::new

    fn PathSegment::new(protocol : Protocol, value : ProtocolValue) -> Result[PathSegment, MoonLoomError]

    PathSegment::not_equal

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

    PathSegment::protocol

    fn PathSegment::protocol(self : PathSegment) -> Protocol

    PathSegment::value

    Protocol

    pub(all) enum Protocol {
    Ip4
    Ip6
    Dns
    Dns4
    Dns6
    Dnsaddr
    Tcp
    Udp
    P2p
    Http
    Https
    Ws
    Wss
    Unknown(UInt64)
    } derive(Eq,
    Debug
    )

    Protocol identifiers used by the first Multiaddr model.

    Protocol::accepts_value

    fn Protocol::accepts_value(self : Protocol, value : ProtocolValue) -> Bool

    Protocol::code

    fn Protocol::code(self : Protocol) -> UInt64

    Protocol::diagnostic_name

    fn Protocol::diagnostic_name(self : Protocol) -> String

    Protocol::equal

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

    Protocol::expected_value_kind

    fn Protocol::expected_value_kind(self : Protocol) -> ProtocolValueKind

    Protocol::from_code

    fn Protocol::from_code(code : UInt64) -> Protocol

    Protocol::from_name

    fn Protocol::from_name(name : StringView) -> Protocol?

    Protocol::name

    fn Protocol::name(self : Protocol) -> String?

    Protocol::not_equal

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

    Protocol::to_repr

    ProtocolInfo

    pub(all) struct ProtocolInfo {
    protocol : Protocol
    code : UInt64
    name : String
    value_kind : ProtocolValueKind
    } derive(Eq,
    Debug
    )

    ProtocolInfo::equal

    ProtocolInfo::not_equal

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

    ProtocolValue

    pub(all) enum ProtocolValue {
    Empty
    Ipv4(Ipv4Address)
    Ipv6(Ipv6Address)
    Domain(String)
    Port(Int)
    PeerId(String)
    Text(String)
    Raw(Bytes)
    } derive(Eq,
    Debug
    )

    Typed values accepted by the Multiaddr domain model.

    ProtocolValue::equal

    ProtocolValue::kind

    ProtocolValue::not_equal

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

    ProtocolValueKind

    pub(all) enum ProtocolValueKind {
    Empty
    Ipv4
    Ipv6
    Domain
    Port
    PeerId
    Text
    Raw
    } derive(Eq,
    Debug
    )

    ProtocolValueKind::equal

    ProtocolValueKind::not_equal

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

    WireReader

    pub struct WireReader {
    input : BytesView
    offset : Int
    limits : Limits
    }

    Bounded binary reader used by protocol codecs.

    WireReader::is_done

    fn WireReader::is_done(self : WireReader) -> Bool

    WireReader::new

    fn WireReader::new(input : BytesView, limits : Limits) -> Result[WireReader, MoonLoomError]

    WireReader::position

    fn WireReader::position(self : WireReader) -> Int

    WireReader::read_fixed

    fn WireReader::read_fixed(self : WireReader, context : String, length : Int) -> Result[BytesView, MoonLoomError]

    WireReader::read_length_prefixed

    fn WireReader::read_length_prefixed(self : WireReader, context : String) -> Result[BytesView, MoonLoomError]

    WireReader::read_u16_be

    fn WireReader::read_u16_be(self : WireReader, context : String) -> Result[Int, MoonLoomError]

    WireReader::read_varint

    fn WireReader::read_varint(self : WireReader, context : String) -> Result[UInt64, MoonLoomError]

    WireReader::remaining

    fn WireReader::remaining(self : WireReader) -> Int

    WireReader::remaining_view

    fn WireReader::remaining_view(self : WireReader) -> BytesView

    WireWriter

    pub struct WireWriter {
    buffer :
    Buffer

    }

    Append-only binary writer used by protocol codecs.

    WireWriter::length

    fn WireWriter::length(self : WireWriter) -> Int

    WireWriter::new

    fn WireWriter::new() -> WireWriter

    WireWriter::to_bytes

    fn WireWriter::to_bytes(self : WireWriter) -> Bytes

    WireWriter::write_byte

    fn WireWriter::write_byte(self : WireWriter, value : Byte) -> Unit

    WireWriter::write_bytes

    fn WireWriter::write_bytes(self : WireWriter, value : BytesView) -> Unit

    WireWriter::write_length_prefixed

    fn WireWriter::write_length_prefixed(self : WireWriter, value : BytesView) -> Unit

    WireWriter::write_u16_be

    fn WireWriter::write_u16_be(self : WireWriter, value : Int) -> Unit

    WireWriter::write_varint

    fn WireWriter::write_varint(self : WireWriter, value : UInt64) -> Unit

    CID_VERSION_0

    let CID_VERSION_0 : UInt64

    CID_VERSION_1

    let CID_VERSION_1 : UInt64

    DAG_PB_CODEC

    let DAG_PB_CODEC : UInt64

    RAW_CODEC

    let RAW_CODEC : UInt64

    Common codec identifier for raw bytes.

    cid_from_content

    fn cid_from_content(codec : UInt64, algorithm : HashAlgorithm, content : BytesView, provider : HashProvider, limits : Limits) -> Result[Cid, MoonLoomError]

    cid_v0_from_content

    fn cid_v0_from_content(content : BytesView, provider : HashProvider, limits : Limits) -> Result[Cid, MoonLoomError]

    codec_table

    fn codec_table() -> Array[CodecInfo]

    decode_u64

    fn decode_u64(input : BytesView) -> Result[(UInt64, Int), MoonLoomError]

    Decode an unsigned varint with the default resource limits.

    decode_u64_with_limits

    fn decode_u64_with_limits(input : BytesView, limits : Limits) -> Result[(UInt64, Int), MoonLoomError]

    Decode one unsigned varint and return the value together with the number of bytes consumed. Trailing bytes are intentionally left to the caller.

    encode_u64

    fn encode_u64(value : UInt64) -> Bytes

    Encode an unsigned 64-bit integer using the Multiformats unsigned varint representation (unsigned LEB128, minimal form).

    multibase_decode

    fn multibase_decode(text : StringView, limits : Limits) -> Result[DecodedMultibase, MoonLoomError]

    multibase_encode

    fn multibase_encode(base : Base, data : BytesView) -> Result[String, MoonLoomError]

    multicodec_code

    fn multicodec_code(name : StringView) -> UInt64?

    multicodec_count

    fn multicodec_count() -> Int

    multicodec_info

    fn multicodec_info(code : UInt64) -> CodecInfo?

    multicodec_name

    fn multicodec_name(code : UInt64) -> String?

    protocol_info

    fn protocol_info(protocol : Protocol) -> ProtocolInfo?

    protocol_info_by_code

    fn protocol_info_by_code(code : UInt64) -> ProtocolInfo?

    protocol_table

    fn protocol_table() -> Array[ProtocolInfo]

    sha2_provider

    fn sha2_provider() -> HashProvider

    Built-in provider backed by the reviewed MoonCrypt SHA-2 implementation.

    supported_bases

    fn supported_bases() -> Array[Base]

    supported_hash_algorithms

    fn supported_hash_algorithms() -> Array[HashAlgorithm]