yabase

Yet Another Base – a unified, type-safe interface for multiple binary-to-text encodings in Gleam.
- Encoding schemes are first-class values.
- Both low-level (direct module) and high-level (unified dispatch) APIs.
- Multibase prefix support for auto-detection.
- Pure Gleam implementation, no external dependencies.
Requirements
- Gleam 1.15 or later
- Erlang/OTP 27 or later
Install
gleam add yabase
Quick start
import yabase
import yabase/core/encoding.{Base64, Standard}
pub fn main() {
// Unified API
let assert Ok(encoded) = yabase.encode(Base64(Standard), <<"Hello":utf8>>)
// encoded == "SGVsbG8="
let assert Ok(decoded) = yabase.decode_as(Base64(Standard), encoded)
// decoded == <<"Hello":utf8>>
}
Supported encodings
Core
| Encoding | Variants |
|---|---|
| Base16 | (hex) |
| Base32 | RFC4648, Hex, Crockford, Clockwork, z-base-32 |
| Base64 | Standard, URL-safe, No padding, URL-safe no padding, DQ (hiragana) |
| Base58 | Bitcoin |
Additional
| Encoding | Description |
|---|---|
| Base36 | 0-9, a-z (case-insensitive decode) |
| Base45 | RFC 9285 (QR-code friendly) |
| Base62 | 0-9, A-Z, a-z |
| Base91 | 91 printable ASCII characters |
| Ascii85 | btoa style |
| Adobe Ascii85 | PDF/PostScript with <~ ~> delimiters |
| Z85 | ZeroMQ variant of Ascii85 |
| RFC 1924 Base85 | RFC 1924 alphabet |
Checksum-bearing (separate API)
These encodings carry metadata (version bytes, checksums, HRP) and have their own API outside the Encoding ADT.
| Encoding | Module | Description |
|---|---|---|
| Base58Check | yabase/base58check | Bitcoin-style: version byte + payload + SHA-256 double-hash checksum |
| Bech32 | yabase/bech32 | BIP 173: raw framing (HRP + base32 data + checksum), not SegWit address validation |
| Bech32m | yabase/bech32 | BIP 350: improved checksum constant, same framing |
API layers
yabase provides three API layers. Choose the one that fits your use case.
1. Low-level modules (direct usage)
Each encoding is accessible directly:
import yabase/base64/standard
import yabase/base32/clockwork
import yabase/base45
let encoded = standard.encode(<<"Hello":utf8>>)
// "SGVsbG8="
let assert Ok(data) = clockwork.decode("91JPRV3F41BPYWKCCGGG")
2. Unified API (dispatch by Encoding type)
import yabase
import yabase/core/encoding.{Base32, Clockwork}
let assert Ok(encoded) = yabase.encode(Base32(Clockwork), <<"Hello":utf8>>)
let assert Ok(decoded) = yabase.decode_as(Base32(Clockwork), encoded)
3. Facade (developer-friendly shortcuts)
import yabase/facade
let encoded = facade.encode_base64(<<"Hello":utf8>>)
let assert Ok(decoded) = facade.decode_base64(encoded)
Multibase support
Prefix-based encoding and auto-detection:
import yabase
import yabase/core/encoding.{Base16, Base58, Decoded}
// Encode with multibase prefix
let assert Ok(prefixed) = yabase.encode_with_prefix(Base16, <<"Hello":utf8>>)
// "f48656c6c6f"
// Decode with auto-detection
let assert Ok(Decoded(encoding: Base16, data: data)) = yabase.decode(prefixed)
Bech32 / Bech32m (BIP 173, BIP 350)
Raw Bech32 framing and checksum. Does not validate SegWit address semantics (witness version, program length):
import yabase/bech32
// Bech32 encode
let assert Ok(encoded) = bech32.encode("bc", <<0, 14, 20, 15>>)
// "bc1..." with 6-char checksum
// Auto-detect Bech32 vs Bech32m on decode
let assert Ok(decoded) = bech32.decode(encoded)
// decoded.hrp == "bc", decoded.variant == Bech32
Base58Check (Bitcoin)
import yabase/base58check
// Encode with version byte 0 (Bitcoin mainnet P2PKH)
let assert Ok(encoded) = base58check.encode(0, <<0xab, 0xcd>>)
// Base58 string with 4-byte SHA-256 checksum
// Decode and verify checksum
let assert Ok(decoded) = base58check.decode(encoded)
// decoded.version == 0, decoded.payload == <<0xab, 0xcd>>
Modules
| Module | Responsibility |
|---|---|
yabase | Top-level unified API: encode, decode_as, encode_with_prefix, decode |
yabase/facade | Developer-friendly shortcut functions for each encoding |
yabase/core/encoding | Type definitions: Encoding, Decoded, CodecError |
yabase/core/dispatcher | Internal dispatch from Encoding to codec modules |
yabase/core/multibase | Multibase prefix encoding and auto-detection |
yabase/base16 | Base16 (hex) |
yabase/base32/* | Base32 variants: rfc4648, hex, crockford, clockwork, zbase32 |
yabase/base64/* | Base64 variants: standard, urlsafe, nopadding, urlsafe_nopadding, dq |
yabase/base36 | Base36 |
yabase/base45 | Base45 (RFC 9285) |
yabase/base58 | Base58 (Bitcoin) |
yabase/base62 | Base62 |
yabase/base91 | Base91 |
yabase/ascii85 | Ascii85 (btoa) |
yabase/adobe_ascii85 | Adobe Ascii85 (PDF/PostScript, <~ ~> delimiters) |
yabase/rfc1924_base85 | RFC 1924 Base85 |
yabase/z85 | Z85 (ZeroMQ) |
yabase/base58check | Base58Check (version byte + SHA-256 checksum) |
yabase/bech32 | Raw Bech32/Bech32m framing and checksum (not SegWit address validation) |
Error handling
Encode and decode functions that can fail return Result(_, CodecError). The concrete return types vary by API:
| Function | Return type |
|---|---|
yabase.encode | Result(String, CodecError) |
yabase.decode_as | Result(BitArray, CodecError) |
yabase.decode | Result(Decoded, CodecError) |
yabase.encode_with_prefix | Result(String, CodecError) |
Low-level *.decode | Result(BitArray, CodecError) |
Low-level *.encode | String (total; except z85/rfc1924_base85 which return Result) |
bech32.encode / bech32.encode_m | Result(String, CodecError) |
bech32.decode | Result(Bech32Decoded, CodecError) |
base58check.encode | Result(String, CodecError) |
base58check.decode | Result(Base58CheckDecoded, CodecError) |
The CodecError type provides specific error information:
| Variant | Returned from | Meaning |
|---|---|---|
InvalidCharacter(character, position) | decode | Input contains a character not in the alphabet |
InvalidLength(length) | encode / decode | Input length is not valid for the encoding |
Overflow | decode | Decoded value overflows the expected range (Base45, Z85, Adobe Ascii85, RFC 1924 Base85) |
UnsupportedPrefix(prefix) | yabase.decode | Unknown multibase prefix during auto-detection |
UnsupportedMultibaseEncoding(name) | encode_with_prefix | Encoding has no assigned multibase prefix (e.g. Base64 DQ) |
InvalidChecksum | base58check.decode, bech32.decode | Checksum verification failed |
InvalidHrp(reason) | bech32.encode, bech32.decode | Invalid human-readable part in Bech32 |
Development
This project uses mise to manage Gleam and Erlang versions, and just as a task runner.
mise install # install Gleam and Erlang
just ci # format check, typecheck, build, test
just test # gleam test
just format # gleam format
just check # all checks without deps download
Contributing
Contributions are welcome. See CONTRIBUTING.md for details.