//// Apiculture - cryptographically secure API-key generation for Gleam. //// //// The top-level module provides the v0.3.0 API for generating, importing, //// parsing, inspecting, and verifying structured API keys. Start here for //// ordinary application use; the lower-level modules are available when a //// more specialized encoding, alphabet, or checksum workflow is needed. import apiculture/alphabet import apiculture/checksum import apiculture/default_key import apiculture/encoding import apiculture/error import apiculture/key import gleam/option.{type Option} // ============================================================================ // Re-exports: Types // ============================================================================ /// A validated character repertoire for direct random character sampling. pub type Alphabet = alphabet.Alphabet /// Errors returned by key generation, parsing, encoding, and validation. pub type Error = error.Error /// A finalized API key, including its serialized value and inspectable sections. pub type Key = key.Key /// The prefix, content, and checksum sections of a finalized key. pub type KeySections = key.KeySections /// A chainable plan for generating or formatting a key. /// /// A `KeyConfig` is not a key. Finish it with `key_generate`, `key_from_bytes`, /// or one of the configured UUID/ULID import functions. pub type KeyConfig = key.KeyConfig /// The parse-relevant shape of a serialized key. /// /// Use `KeyFormat` with `key_parse_with_format`; generation intent is kept in /// `KeyConfig` because it cannot be recovered from a serialized value. pub type KeyFormat = key.KeyFormat /// A byte-to-text encoding used for key content and checksum sections. pub type Encoding = encoding.Encoding /// A checksum algorithm used to validate key content. pub type Checksum = checksum.Checksum // ============================================================================ // Re-exports: Error constructors // ============================================================================ /// Returns the error for an empty custom alphabet. pub fn empty_alphabet_error() -> error.Error { error.EmptyAlphabet } /// Returns the error for a one-character custom alphabet. pub fn single_character_alphabet_error() -> error.Error { error.SingleCharacterAlphabet } /// Returns the error for a custom alphabet containing duplicate characters. pub fn duplicate_characters_error() -> error.Error { error.DuplicateCharacters } /// Returns the error raised when secure randomness is unavailable. pub fn secure_random_unavailable_error() -> error.Error { error.SecureRandomUnavailable } /// Returns the error for an invalid or incomplete byte/character count. pub fn invalid_byte_count_error() -> error.Error { error.InvalidByteCount } /// Returns the error for an invalid encoding configuration. pub fn invalid_encoding_error() -> error.Error { error.InvalidEncoding } /// Returns the error for malformed encoded, UUID, ULID, or serialized input. pub fn malformed_input_error() -> error.Error { error.MalformedInput } /// Returns the error for a checksum that does not match its content. pub fn checksum_mismatch_error() -> error.Error { error.ChecksumMismatch } // ============================================================================ // Alphabet functions // ============================================================================ /// Creates a validated custom alphabet for direct character sampling. /// /// The alphabet must contain at least two unique graphemes. pub fn new_alphabet(chars: String) -> Result(Alphabet, error.Error) { alphabet.new_alphabet(chars) } /// Returns the graphemes in a validated alphabet. pub fn alphabet_characters(alphabet: Alphabet) -> List(String) { alphabet.characters(alphabet) } /// Returns the number of graphemes in an alphabet. pub fn alphabet_size(alphabet: Alphabet) -> Int { alphabet.size(alphabet) } // ============================================================================ // Key functions // ============================================================================ /// Generates one key using the v0.3.0 default format. /// /// The default is `sk__crc32_` with 16 random bytes encoded /// as Base62 and a four-byte CRC32 checksum. pub fn key_new() -> Result(Key, error.Error) { key.new_default() } /// Generates and serializes one key using the v0.3.0 default format. /// /// ## Examples /// /// ```gleam /// let assert Ok(value) = key_new_as_string() /// ``` pub fn key_new_as_string() -> Result(String, error.Error) { key.new_as_string() } /// Returns a ready-to-generate configuration containing the default settings. /// /// Use the `key_with_*` and `key_disable_*` functions to customize it. /// /// ## Examples /// /// ```gleam /// let assert Ok(key) = /// key_new_as_config() /// |> key_with_prefix("partner") /// |> key_generate /// ``` pub fn key_new_as_config() -> KeyConfig { key.new_as_config() } /// Finalizes a random-generation configuration as a `Key`. /// /// This is a terminal operation. It generates fresh content according to the /// selected mode and retains the rendered sections for later inspection. pub fn key_generate(config: KeyConfig) -> Result(Key, error.Error) { key.generate(config) } /// Reports whether a configuration contains one valid content-generation mode. pub fn key_config_is_ready(config: KeyConfig) -> Bool { key.is_ready(config) } /// Formats existing bytes using a key configuration. /// /// This is the terminal step for parse-then-format UUID and ULID workflows. pub fn key_from_bytes( config: KeyConfig, bytes: BitArray, ) -> Result(Key, error.Error) { key.from_bytes(config, bytes) } /// Parses a UUID into its canonical 16 bytes. pub fn bytes_from_uuid(input: String) -> Result(BitArray, error.Error) { key.bytes_from_uuid(input) } /// Parses a ULID into its canonical 16 bytes. pub fn bytes_from_ulid(input: String) -> Result(BitArray, error.Error) { key.bytes_from_ulid(input) } /// Imports a UUID using the v0.3.0 default key format. /// /// This is a terminal operation. Use `bytes_from_uuid` followed by /// `key_from_bytes` when the formatting pipeline needs to remain explicit. pub fn key_from_uuid(input: String) -> Result(Key, error.Error) { key.from_uuid(input) } /// Imports a UUID and formats it with a supplied configuration. pub fn key_from_uuid_with_config( config: KeyConfig, input: String, ) -> Result(Key, error.Error) { key.from_uuid_with_config(config, input) } /// Imports a ULID using the v0.3.0 default key format. pub fn key_from_ulid(input: String) -> Result(Key, error.Error) { key.from_ulid(input) } /// Imports a ULID and formats it with a supplied configuration. pub fn key_from_ulid_with_config( config: KeyConfig, input: String, ) -> Result(Key, error.Error) { key.from_ulid_with_config(config, input) } /// Parses and verifies a key in the strict default serialized format. /// /// A successful result has canonical Base62 content, the expected `sk` prefix, /// 16 content bytes, and a matching four-byte CRC32 checksum. pub fn key_parse(input: String) -> Result(Key, error.Error) { key.parse(input) } /// Returns the parse format corresponding to the v0.3.0 default. pub fn key_default_format() -> KeyFormat { key.default_format() } /// Parses and verifies a key using an explicitly supplied format. pub fn key_parse_with_format( input: String, format: KeyFormat, ) -> Result(Key, error.Error) { key.parse_with_format(input, format) } /// Returns the serialized value of a finalized key. /// /// Prefer `key_to_string` in pipelines; this name remains for compatibility. pub fn key_value(k: Key) -> String { key.value(k) } /// Returns the serialized value of a finalized key. pub fn key_to_string(k: Key) -> String { key.value(k) } /// Returns the raw content bytes of a finalized key. /// /// This is a compatibility alias for `key_content_bytes`. pub fn key_bytes(k: Key) -> BitArray { key.bytes(k) } /// Returns the optional serialized prefix. pub fn key_prefix_value(k: Key) -> Option(String) { key.prefix_value(k) } /// Returns the optional structural separator. pub fn key_separator_value(k: Key) -> Option(String) { key.separator_value(k) } /// Reports whether the key has a prefix. pub fn key_has_prefix(k: Key) -> Bool { key.has_prefix(k) } /// Reports whether the key has a checksum. pub fn key_has_checksum(k: Key) -> Bool { key.has_checksum(k) } /// Returns the encoded content section without prefix or checksum sections. pub fn key_content_value(k: Key) -> String { key.content_value(k) } /// Returns the raw bytes represented by the content section. pub fn key_content_bytes(k: Key) -> BitArray { key.content_bytes(k) } /// Returns the number of raw content bytes. pub fn key_content_byte_count(k: Key) -> Int { key.content_byte_count(k) } /// Returns the number of characters in the encoded content section. pub fn key_content_char_count(k: Key) -> Int { key.content_char_count(k) } /// Returns the optional lowercase checksum algorithm name. pub fn key_checksum_name(k: Key) -> Option(String) { key.checksum_name(k) } /// Returns the optional encoded checksum section. pub fn key_checksum_value(k: Key) -> Option(String) { key.checksum_value(k) } /// Returns the optional raw checksum bytes. pub fn key_checksum_bytes(k: Key) -> Option(BitArray) { key.checksum_bytes(k) } /// Returns the checksum byte count, or `0` when no checksum is present. pub fn key_checksum_byte_count(k: Key) -> Int { key.checksum_byte_count(k) } /// Returns the encoded checksum character count, when present. pub fn key_checksum_char_count(k: Key) -> Option(Int) { key.checksum_char_count(k) } /// Verifies a key with the default CRC32 algorithm. pub fn key_verify_checksum(k: Key) -> Bool { key.verify_default_checksum(k) } /// Verifies a key with an explicitly supplied checksum algorithm. pub fn key_verify_checksum_with_algo(k: Key, csum: Checksum) -> Bool { key.verify_checksum(k, csum) } /// Returns all serialized and raw key sections as one value. pub fn key_sections(k: Key) -> key.KeySections { key.sections(k) } // Builder functions /// Selects secure random byte generation with the given byte count. pub fn key_with_random_bytes(config: KeyConfig, count: Int) -> KeyConfig { key.with_random_bytes(config, count) } /// Selects direct alphabet sampling with the given character count. /// /// Pair this with `key_with_alphabet`; it is distinct from generating random /// bytes and then encoding them. pub fn key_with_random_chars(config: KeyConfig, count: Int) -> KeyConfig { key.with_random_chars(config, count) } /// Sets the alphabet used by direct character sampling. pub fn key_with_alphabet(config: KeyConfig, alphabet: Alphabet) -> KeyConfig { key.with_alphabet(config, alphabet) } /// Sets the encoding used for content and checksum sections. pub fn key_with_encoding(config: KeyConfig, encoding: Encoding) -> KeyConfig { key.with_encoding(config, encoding) } /// Sets the serialized key prefix. pub fn key_with_prefix(config: KeyConfig, prefix: String) -> KeyConfig { key.with_prefix(config, prefix) } /// Sets the separator used at every structural boundary. pub fn key_with_separator(config: KeyConfig, separator: String) -> KeyConfig { key.with_separator(config, separator) } /// Sets the checksum algorithm and serialized algorithm name. pub fn key_with_checksum(config: KeyConfig, checksum: Checksum) -> KeyConfig { key.with_checksum(config, checksum) } /// Sets the number of checksum bytes retained in the key. pub fn key_with_checksum_bytes(config: KeyConfig, count: Int) -> KeyConfig { key.with_checksum_bytes(config, count) } /// Removes the prefix from a key configuration. pub fn key_disable_prefix(config: KeyConfig) -> KeyConfig { key.disable_prefix(config) } /// Removes the checksum name while retaining the checksum. pub fn key_disable_checksum_name(config: KeyConfig) -> KeyConfig { key.disable_checksum_name(config) } /// Removes both the checksum and its serialized name. pub fn key_disable_checksum(config: KeyConfig) -> KeyConfig { key.without_checksum(config) } /// Changes the expected prefix in a parser format. pub fn key_format_with_prefix(format: KeyFormat, prefix: String) -> KeyFormat { key.format_with_prefix(format, prefix) } /// Removes the expected prefix from a parser format. pub fn key_format_without_prefix(format: KeyFormat) -> KeyFormat { key.format_without_prefix(format) } /// Changes the expected separator in a parser format. pub fn key_format_with_separator( format: KeyFormat, separator: String, ) -> KeyFormat { key.format_with_separator(format, separator) } /// Changes the expected encoding in a parser format. pub fn key_format_with_encoding( format: KeyFormat, encoding: Encoding, ) -> KeyFormat { key.format_with_encoding(format, encoding) } /// Changes the expected checksum algorithm in a parser format. pub fn key_format_with_checksum( format: KeyFormat, checksum: Checksum, ) -> KeyFormat { key.format_with_checksum(format, checksum) } /// Removes the expected checksum name from a parser format. pub fn key_format_without_checksum_name(format: KeyFormat) -> KeyFormat { key.format_without_checksum_name(format) } /// Removes the expected checksum from a parser format. pub fn key_format_without_checksum(format: KeyFormat) -> KeyFormat { key.format_without_checksum(format) } /// Changes the expected checksum byte count in a parser format. pub fn key_format_with_checksum_bytes( format: KeyFormat, count: Int, ) -> KeyFormat { key.format_with_checksum_bytes(format, count) } /// Changes the expected content byte count in a parser format. pub fn key_format_with_content_bytes( format: KeyFormat, count: Int, ) -> KeyFormat { key.format_with_content_bytes(format, count) } // ============================================================================ // Encoding functions // ============================================================================ /// Encodes bytes with an apiculture encoding. pub fn encoding_encode(enc: Encoding, bytes: BitArray) -> String { encoding.encode(enc, bytes) } /// Decodes a string with an apiculture encoding. pub fn encoding_decode( enc: Encoding, input: String, ) -> Result(BitArray, error.Error) { encoding.decode(enc, input) } /// Reports whether an encoding uses padding. pub fn encoding_uses_padding(enc: Encoding) -> Bool { encoding.uses_padding(enc) } /// Returns the multibase name of an encoding. pub fn encoding_name(enc: Encoding) -> String { encoding.name(enc) } // Hex encodings /// Returns lowercase hexadecimal encoding. pub fn hex_lower() -> Encoding { encoding.hex_lower() } /// Returns uppercase hexadecimal encoding. pub fn hex_upper() -> Encoding { encoding.hex_upper() } // Base32 encodings /// Returns padded RFC 4648 Base32 encoding. pub fn base32_rfc() -> Encoding { encoding.base32_rfc() } /// Returns unpadded RFC 4648 Base32 encoding. pub fn base32_rfc_unpadded() -> Encoding { encoding.base32_rfc_unpadded() } /// Returns Base32hex encoding. pub fn base32_hex() -> Encoding { encoding.base32_hex() } /// Returns Crockford Base32 encoding. pub fn base32_crockford() -> Encoding { encoding.base32_crockford() } /// Returns z-base-32 encoding. pub fn base32_z() -> Encoding { encoding.base32_z() } // Base36 encoding /// Returns lowercase Base36 encoding. pub fn base36() -> Encoding { encoding.base36() } // Base58 encoding /// Returns Bitcoin-style Base58 encoding. pub fn base58() -> Encoding { encoding.base58() } // Base62 encoding /// Returns Base62 encoding. pub fn base62() -> Encoding { encoding.base62() } // Base64 encodings /// Returns standard padded Base64 encoding. pub fn base64() -> Encoding { encoding.base64() } /// Returns URL-safe padded Base64 encoding. pub fn base64_url() -> Encoding { encoding.base64_url() } // ============================================================================ // Checksum functions // ============================================================================ /// Formats a checksum using a caller-provided byte encoding function. pub fn checksum_format( csum: Checksum, bytes: BitArray, format_with: fn(BitArray) -> String, ) -> String { checksum.format(csum, bytes, format_with) } /// Verifies raw bytes against an expected checksum. pub fn checksum_verify( csum: Checksum, bytes: BitArray, expected: BitArray, ) -> Bool { checksum.verify(csum, bytes, expected) } /// Returns the CRC32 checksum algorithm used by the default key format. pub fn crc32() -> Checksum { checksum.crc32_checksum() } // ============================================================================ // Default key functions // ============================================================================ /// Compatibility alias for generating a v0.3.0 default key. /// /// Prefer `key_new()` in new code. This function returns the same structured /// `sk__crc32_` format. pub fn default_generate() -> Result(Key, error.Error) { default_key.generate() } /// Generates a v0.3.0 structured key with a custom prefix. /// /// The format is `__crc32_`. /// /// The prefix should be a short semantic identifier (e.g., "sk", "token", "key"). pub fn default_prefixed(prefix: String) -> Result(Key, error.Error) { default_key.prefixed(prefix) }