reckon_gater_stream_id (reckon_gater v2.3.1)
View SourceStream-id format validator + generator — the protocol contract.
Single source of truth for what "a valid stream id" means across the reckon-db ecosystem. Lives in reckon-gater so it is reachable from both reckon-db (write-time validation) and reckon-evoq (adapter / dispatch-time generation) without dragging reckon-db's khepri/Ra payload into pure-routing consumers.
Accepted formats
- **User stream:**
<prefix>-<hex>, where:<prefix>is[a-z]{1,32}— one to thirty-two ASCII lowercase letters. No digits, no hyphens, no$, no uppercase.-is a single mandatory separator.<hex>is[a-f0-9]{32}— exactly thirty-two lowercase hex digits. One UUID-worth of bits (128).
order-018f6a7b8c9d4e5f60718293a4b5c6d7,sess-005774fd728b1b2866cd18ff294467e1. - **System stream:**
$<namespace>:<name>, where:$is a mandatory prefix.<namespace>is[a-z][a-z0-9-]*— lowercase identifier, may contain hyphens (e.g.link-sub).:is a single mandatory separator.<name>is[A-Za-z0-9][A-Za-z0-9_.-]*— intentionally human-readable; system streams exist for operational legibility.
$link:high-value-orders,$link-sub:revenue.
History
v2.2.0 tightened the user-stream regex from ^[A-Za-z]+-[A-Fa-f0-9]+$ to ^[a-z]{1,32}-[a-f0-9]{32}$ and added the new/1 generator. Motivation: the permissive regex admitted a-0, Order-DEADBEEF, and demo-1779045695... — inconsistent shapes that broke logging, projection grouping, and downstream tooling. Lowercase + fixed-length 32-hex suffix gives every user id 128 bits of entropy and a predictable length.
Migration: previously stored ids that don't conform are still readable (validate/1 is only called from reckon_db_streams:append/4); they just can't accept new events. Since reckon-db has no production deployments at this point, no migration path is required.
Rejected (returns `{error, Reason}')
- Empty binary —
empty - Not a binary —
not_binary - Anything starting with
$that isn't a well-formed system id —malformed_system_id - Anything not starting with
$that isn't a well-formed user id —malformed_user_id
Rejected examples: a-0 (suffix too short), Order-DEADBEEF (uppercase + suffix too short), account- (empty suffix), -deadbeef (empty prefix), test$basic-stream (mid-string $).
Summary
Types
Note: invalid_prefix is NOT a validation_error/0 — it is the reason class raised by new/1 via erlang:error/1 when the supplied prefix is malformed. validate/1 itself never sees a prefix in isolation, so it cannot produce that variant.
Functions
True if StreamId is in the system namespace (starts with $ and is well-formed). Note: $all is NOT a valid stream id — it's a subscription-selector sentinel only.
Boolean wrapper for use in guards / list-comprehensions.
Build a fresh, valid user stream id with the given prefix.
Extract the prefix segment from a well-formed user stream id. Returns undefined for system ids and malformed ids.
Extract the hex-suffix segment from a well-formed user stream id. Returns undefined for system ids and malformed ids.
Validate StreamId. Returns ok if it matches either accepted format, or {error, Reason}.
Types
-type validation_error() :: empty | not_binary | malformed_user_id | malformed_system_id.
Note: invalid_prefix is NOT a validation_error/0 — it is the reason class raised by new/1 via erlang:error/1 when the supplied prefix is malformed. validate/1 itself never sees a prefix in isolation, so it cannot produce that variant.
Functions
True if StreamId is in the system namespace (starts with $ and is well-formed). Note: $all is NOT a valid stream id — it's a subscription-selector sentinel only.
Boolean wrapper for use in guards / list-comprehensions.
-spec new(prefix()) -> nonempty_binary().
Build a fresh, valid user stream id with the given prefix.
Suffix is the 16-byte UUIDv7 from reckon_gater_uuid:v7/0 rendered as 32 lowercase hex chars — so the id is BOTH regex-compliant AND time-sortable.
Prefix must match [a-z]{1,32}; raises {invalid_prefix, Prefix} otherwise. Accepts atom or binary.
Examples:
reckon_gater_stream_id:new(<<"sess">>).
%% => <<"sess-019d7a4f3c2a7d8c9e0f1234567890ab">>
reckon_gater_stream_id:new(order).
%% => <<"order-019d7a4f3c2a7d8c9e0f1234567890ab">>
Extract the prefix segment from a well-formed user stream id. Returns undefined for system ids and malformed ids.
Extract the hex-suffix segment from a well-formed user stream id. Returns undefined for system ids and malformed ids.
-spec validate(term()) -> ok | {error, validation_error()}.
Validate StreamId. Returns ok if it matches either accepted format, or {error, Reason}.