Viewable ID
viewable_id is the public product identifier exposed alongside the
internal UUIDv7 canonical_products.id. Format: TR-XXXXXXX where
XXXXXXX is 7 characters from the Crockford base32 alphabet
(0-9A-HJKMNP-TV-Z, letters I/L/O/U excluded to avoid visual
collision).
Regex: ^TR-[0-9A-HJKMNP-TV-Z]{7}$.
Why not just the UUID?
- Voice dictation: UUIDs mispell over the phone;
TR-8F2K3P2reads cleanly. - Paper catalogues: printed IDs often lose
0/Oor1/I/Lglyph fidelity. Crockford alphabet dodges both. - URL ergonomics: short path component improves bookmarks + QR codes.
Lookup semantics
GET /v1/products/{id_or_viewable} accepts either the UUID or the
viewable form. Handler dispatches on viewableid.ValidFormat to the
appropriate repository method.
Stability
- Assigned at canonical create via
ViewableIDIssuer.Next. - Immutable after create.
- Survives canonical merges: alias → survivor edge in
canonical_aliasespreserves both IDs; both resolve to the same surviving product.
Collision handling
platform/viewableid.Generator.Next uses crypto/rand and retries up
to 3× on UniqueChecker hit. Space is 32⁷ ≈ 34·10⁹ — collision
probability at 10⁵ products is ~1.5·10⁻⁵ per issuance; 3 retries
drive it to ~10⁻¹⁵.
ErrCollisionsExhausted surfaces only when the UniqueChecker is
degenerate (e.g. always returns “taken”). Production wiring reads
uniqueness from canonical_products.viewable_id UNIQUE — if this
error fires, investigate the check path before blaming randomness.
Implementation
backend/internal/platform/viewableid/— Generator, UniqueChecker port, ErrCollisionsExhausted.backend/internal/core/catalog/canonical/app.ViewableIDIssuer— consumer-specific wrapper plugging canonical_products.viewable_id UNIQUE as the check source.
References
- ADR-0032 viewable-id scheme
- spec
docs/superpowers/specs/2026-04-20-ingestion-phase1-a-design.md§5.5