Chooses identifier generation and public exposure rules so sortable IDs do not leak unintended creation timing, tenant sequencing, or volume signals through URLs, logs, and APIs.
Package status: AI-assisted reference package. Static package validation has passed; implementation scenarios remain not run and human domain review is required before stable adoption.
Decision
Primary keys and public resource identifiers must be chosen as one policy, not as separate defaults. When the datastore benefits from time-ordered clustering, the team may adopt a sortable identifier format at persistence boundaries while keeping a separate opaque surrogate for any customer-visible URL or share link. Random identifiers remain valid where ordering offers no operational benefit and exposure risk is already low. The decision binds three concerns together: how identifiers are generated, whether their byte order implies creation sequence, and which surfaces may reveal the raw value versus a redacted or hashed form.
Scope
- Identifier generation strategy, collision handling, and migration from legacy formats.
- Sort and pagination fields exposed in list APIs.
- Public URLs, API response fields, structured logs, and analytics exports.
- Enumeration resistance for guessable or sequential public tokens.
Outside this block
- Distributed ID allocation at planetary scale (snowflake-style workers).
- Authentication token formats and session identifiers.
- Database sharding key selection beyond identifier ordering effects.
Contract
- Every persisted entity declares one canonical identifier field and, when needed, a separate public surrogate; the surrogate is the only value permitted in browser-visible URLs.
- Sortable identifier formats may be used for storage indexing, but list APIs sort on an explicit timestamp or cursor field rather than inviting clients to infer order from raw identifier bytes.
- A collision on insert consumes one attempt from the profile collision_retry_limit, which counts total insert attempts including the initial generation; after the limit is exhausted the write fails closed with a conflict error and no silent overwrite.
- Structured logs and support exports store at most a configured hash prefix of the raw identifier unless the consuming system is on an approved break-glass path with audit.
- Cross-tenant resource lookup by identifier or surrogate rejects existence hints for out-of-scope tenants at the same error shape as not-found when enumeration protection is enabled.
- During migration, legacy identifier formats remain readable for a declared dual-read window; responses expose both canonical and legacy alias fields without treating either as proof of ownership.
Implementation guidance
- Generate identifiers in the application tier or a dedicated ID service so the policy is testable independent of ORM defaults.
- If time-ordered formats are chosen, document the timestamp precision embedded in the identifier and verify it cannot be reversed from public surrogates.
- Bind pagination cursors to signed tuples of sort field and identifier rather than exposing raw monotonic sequences to clients.
Failure handling and safeguards
- Clock rollback or generator version mismatch must not mint duplicate values or silently break the declared UUIDv7/sortability contract. Use a monotonic UUIDv7 fallback with a randomized tail that preserves the version and ordering policy; if that implementation is unavailable, fail the write closed and alert rather than emitting UUIDv4 into a UUIDv7-only column.
- Never log full identifiers in web server access logs when the profile requires redaction; hash at ingestion if downstream tools cannot filter.
- Rate-limit unauthenticated lookup by surrogate and by identifier prefix to reduce scanning of share links.
Verification and operations
- Measure collision retry rate, enumeration throttle hits, and dual-read resolution failures during migration.
- Test that public URLs, API payloads, and log lines each follow the exposure matrix in
uuid-policy.yaml. - Prove list endpoints do not emit monotonic public sequence numbers when enumeration protection is enabled.
- Simulate clock rollback and verify every successful fallback remains valid UUIDv7 and nondecreasing within the generator's documented scope.
The executable-looking examples in this package are fixtures and acceptance contracts. Run static validation on the package structure and metadata, then implement and execute the scenarios in the target repository.
Adoption assumptions
- Names and numeric values in
example.yamlare an adoption profile, not universal defaults. - The adopting team must map actors, data classes, error vocabulary, and ownership to its system.
- RFC 9562 terminology is used descriptively; the team selects concrete generator libraries appropriate to its stack.