Flametrench Identifier Format
This document is the normative specification for identifiers issued by any Flametrench-compliant implementation. SDKs in every language must produce identical wire-format strings for the same inputs and must reject identical inputs as invalid.
Design goals
- Self-describing on the wire. A developer reading a log line must be able to tell what kind of resource an ID refers to without consulting the API.
- Sortable by creation time. Time-ordered IDs preserve insertion order in indexes without a separate timestamp column.
- Storage-friendly. The storage representation is a standard UUID that every database, ORM, and language already handles natively.
- Collision-resistant without coordination. Independent processes must be able to generate IDs concurrently without a central coordinator.
- Unambiguous. No character in the wire format is visually confusable with another. No ambiguity at URL or line boundaries.
- Stable across languages. A PHP SDK, a Node SDK, a Go SDK, and any future SDK must all produce byte-identical encodings for the same type and UUID.
Storage format
Identifiers are stored in database columns as UUIDv7 in canonical hyphenated form:
0190f2a8-1b3c-7abc-8123-456789abcdef
Postgres implementations MUST use the native uuid column type. Implementations targeting databases without a native UUID type MUST use a fixed-width binary or 36-character string column with a unique index.
UUIDv7 is specified by RFC 9562. Implementations MUST generate UUIDv7 values using a library with a maintained, reviewed implementation. Implementations MUST NOT generate UUIDv7 values by hand-rolling timestamp and random byte concatenation.
Wire format
Identifiers appear on the wire as:
{type}_{hex}
Where:
{type}is a lowercase ASCII type prefix from the registry below._is a literal underscore character.{hex}is the storage UUID with hyphens stripped, rendered in lowercase.
Example:
Storage: 0190f2a8-1b3c-7abc-8123-456789abcdef
Wire: usr_0190f2a81b3c7abc8123456789abcdef
Why underscore, not hyphen
Hyphens are ambiguous at the end of a URL. When an identifier appears at the end of a line in an email, a chat message, or an auto-linked log entry, a trailing hyphen may be elided or interpreted as a soft break. Underscores survive all of these boundaries intact.
Hyphens also already appear in canonical UUID form. Using underscore as the type separator eliminates parser ambiguity.
Why lowercase hex
Consistent casing guarantees byte-identical encodings across SDKs and simplifies comparison. Implementations MUST emit lowercase and MUST reject uppercase-hex payloads during decoding. A strict decoder surfaces encoding bugs in sister SDKs quickly.
Type prefix registry
The following type prefixes are currently registered by the specification:
| Prefix | Resource | Capability |
|---|---|---|
usr | User | Identity |
ses | Session | Identity |
cred | Credential | Identity |
mfa | Multi-factor factor | Identity (v0.2; ADR 0008) |
org | Organization | Tenancy |
mem | Membership | Tenancy |
inv | Invitation | Tenancy |
tup | Authorization tuple | Authorization |
shr | Share token | Authorization (v0.2; ADR 0012) |
pat | Personal access token | Identity (v0.3; ADR 0016) |
Implementations MUST NOT invent new type prefixes. New prefixes are added by amending this document through the specification's change process.
Personal access token wire format (v0.3)
PAT identifiers use a two-part wire format distinct from other types:
pat_<32hex-id>_<base64url-secret>
The pat_<32hex> portion is the stored ID (a standard {type}_{32hex} Flametrench identifier). The _<base64url-secret> suffix carries the opaque secret component that proves possession. The bearer token routing layer recognizes the pat_ prefix and dispatches to the PAT verifier before session or share verifiers. See ADR 0016 and the Identity chapter for the full verification semantics.
Reserved prefixes for future capabilities (not usable in current implementations):
| Prefix | Planned resource | Capability |
|---|---|---|
aud | Audit event | Audit (v0.4, proposed) |
not | Notification | Notifications |
file | File | Files |
flag | Feature flag | Feature flags |
sub | Subscription | Billing |
Prefix selection rules for future additions:
- 2 to 6 characters.
- Lowercase ASCII letters only (
[a-z]). - Pronounceable when possible.
- Unambiguous when skimming logs alongside existing prefixes.
- Not a substring of any other prefix (prevents accidental match in string searches).
Encoding rules
An implementation's encode(type, uuid) function:
- MUST reject the input if
typeis not in the current registered prefix set, raising the SDK's equivalent ofInvalidTypeError. - MUST reject the input if
uuidis not a valid UUID, raising the SDK's equivalent ofInvalidIdError. - MUST NOT verify that the UUID is specifically UUIDv7. Older UUID versions MAY appear in backfilled data; version checking happens during generation, not encoding.
- MUST strip all hyphens from the UUID and render the result in lowercase.
- MUST return the string
{type}_{hex}.
Decoding rules
An implementation's decode(id) function:
- MUST locate the first
_character. If none exists, raiseInvalidIdError. - MUST verify that the prefix before the separator is in the registered set, raising
InvalidTypeErrorotherwise. - MUST verify that the payload after the separator is exactly 32 characters of lowercase hex (
[0-9a-f]{32}), raisingInvalidIdErrorotherwise. Uppercase hex MUST be rejected. - MUST reconstruct the canonical UUID form by inserting hyphens at positions 8, 12, 16, and 20 of the payload.
- MUST verify that the reconstructed UUID's version nibble (position 13 of the hyphenated form, i.e. the first character of the third group) is one of
1through8, raisingInvalidIdErrorotherwise. This explicitly rejects the Nil UUID (version 0) and the Max UUID (version 15 /f), which some general-purpose UUID validators accept but which are not valid Flametrench identifiers. - MUST return a structured result containing the type and the canonical UUID string.
Generation
An implementation's generate(type) function:
- MUST verify that
typeis in the registered prefix set. - MUST generate a fresh UUIDv7.
- MUST return the result of
encode(type, new_uuid).
Generated identifiers are sortable by creation time by virtue of UUIDv7's structure. Implementations MAY rely on this for ordering in lists, but applications that require strict time ordering across a distributed system SHOULD use explicit timestamp columns in addition to IDs.
Conformance fixtures
The following fixtures are part of the conformance suite. Every SDK MUST produce byte-identical encodings for these inputs:
| Type | UUID | Wire format |
|---|---|---|
usr | 0190f2a8-1b3c-7abc-8123-456789abcdef | usr_0190f2a81b3c7abc8123456789abcdef |
org | 01000000-0000-7000-8000-000000000000 | org_01000000000070008000000000000000 |
ses | 01ffffff-ffff-7fff-bfff-ffffffffffff | ses_01ffffffffff7fffbfffffffffffffff |
And MUST reject the following inputs as invalid:
| Input | Reason |
|---|---|
usr0190f2a81b3c7abc8123456789abcdef | Missing separator |
xyz_0190f2a81b3c7abc8123456789abcdef | Unregistered type prefix |
usr_0190f2a8 | Payload too short |
usr_0190f2a81b3c7abc8123456789abcdef0000 | Payload too long |
usr_0190F2A81B3C7ABC8123456789ABCDEF | Uppercase hex |
usr_0190f2a81b3c7abc8123456789abcdeg0 | Non-hex character |
usr_ | Empty payload |
| empty string | Empty input |
usr_00000000000000000000000000000000 | Nil UUID (version nibble = 0) |
usr_ffffffffffffffffffffffffffffffff | Max UUID (version nibble = f) |
Change history
- v0.3 (2026) — Added
patprefix for personal access tokens (ADR 0016). Go SDK family added as the fifth first-party implementation (ADR 0018). - v0.2 (2026) — Added
mfaandshrprefixes (ADRs 0008, 0012). Postgres reference adapters added across all four original SDK families. - v0.1 (2026) — Initial specification. Registered prefixes for identity, tenancy, authorization.