id

package
v0.26.21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 25, 2026 License: MIT Imports: 2 Imported by: 0

README

id

Package id declares the SDK's identifier-generation port: the Generator contract and the typed Scheme registry. Concrete schemes (UUIDv4/v7, ULID, snowflake, NanoID, KSUID) live in internal/service/app/id and self-register on import; TypeID lives there too but needs a caller-supplied type prefix, so it is built with NewTypeID rather than registered. The public facade is pkg/v1/app/id.

uid, err := id.New("uuidv7")   // dispatch by scheme

See CLAUDE.md for the maintainer contract. ADR 0024.

The Generator port is generated from design/app/id.yaml into design_gen.go (ADR 0163): it changes in the design, then kit gen.

Documentation

Overview

Package id — ranges 0.2.7.* (ADR 0024 core/app/id block) and 0.3.39.* (ADR 0024 service/app/id block, declared here since ADR 0160).

Package id — declares the sentinels of the domain: those the Generator facade returns, and those the concrete schemes raise. A registry sentinel's name equals its errs.Define Reason in SCREAMING_SNAKE form; a scheme sentinel's Reason is its Code constant's name, minus Code, in the same form (ADR 0020's namespaced derivation: CodeIDMalformed → ID_MALFORMED).

Package id declares the identifier-generation port of the SDK: the Generator contract and the typed Scheme string under which a generator registers. It is a core sibling beside codec, writer, crypto, logger, transform, and proc (ADR 0024) and mirrors transform exactly — the registry resolves a Scheme to a Generator the way transform resolves an Algorithm to a Compressor.

No generation bodies live here; concrete generators (UUIDv4/UUIDv7/ULID/ snowflake/NanoID/KSUID) live under internal/service/app/id/ and self-register via a package-level var initialiser when imported — no init(). TypeID lives there too but is NOT registered: it carries a caller-chosen type prefix, and there is no prefix the SDK could invent on their behalf, so New("typeid") returns UnknownScheme by design. The canonical external form of every identifier is its string rendering, so Generator.New returns a string (UUIDs dashed-hex, ULID Crockford base32, snowflake decimal, KSUID base62, NanoID URL-safe).

Package id — holds the process-wide Generator registry. Service-level scheme packages register themselves via package-level var initialisers when imported (no init()), mirroring core/data/codec, core/crypto, and core/data/transform.

Index

Constants

View Source
const CodeDuplicateRegistration errs.Code = 0x00_02_07_05 // 0.2.7.5

CodeDuplicateRegistration identifies a boot-time collision on the Generator registry: a nil scheme, or a distinct generator claiming an already-registered Scheme. Surfaced via panic at boot (see registry.go). Reason DUPLICATE_REGISTRATION keeps the bracket header a valid (code,reason) pairing per ADR 0005, mirroring codec/transform (0.2.2.1 / 0.2.5.5).

View Source
const CodeIDClockBackwards errs.Code = 0x00_03_27_02 // 0.3.39.2

CodeIDClockBackwards identifies a snowflake generation where the monotonic clock moved backwards past the last-issued timestamp beyond the recoverable same-millisecond sequence space.

View Source
const CodeIDClockStalled errs.Code = 0x00_03_27_03 // 0.3.39.3

CodeIDClockStalled identifies a snowflake same-millisecond overflow wait that gave up because the clock stopped advancing. Distinct from ClockBackwards: the clock did not regress, it simply never ticked, so the wait could not complete.

View Source
const CodeIDEntropyFailed errs.Code = 0x00_03_27_01 // 0.3.39.1

CodeIDEntropyFailed identified a crypto/rand.Read failure while drawing the random bytes of an identifier. Nothing returns it since Go 1.24, whose crypto/rand.Read cannot fail; the code stays allocated and published.

View Source
const CodeIDInvalidPrefix errs.Code = 0x00_03_27_05 // 0.3.39.5

CodeIDInvalidPrefix identifies a TypeID constructor or parse call whose type prefix is empty or breaks the lowercase-ASCII shape. The prefix IS the value of the scheme — an identifier that cannot name its own type is the degraded identifier ADR 0031 forbids minting silently.

View Source
const CodeIDInvalidSize errs.Code = 0x00_03_27_04 // 0.3.39.4

CodeIDInvalidSize identifies a NanoID constructor call naming a non-positive identifier length. A zero-length identifier identifies nothing, so the constructor refuses rather than hand back a generator that mints "" forever (ADR 0031).

View Source
const CodeIDMalformed errs.Code = 0x00_03_27_06 // 0.3.39.6

CodeIDMalformed identifies a parse that rejected its input: a wrong length, a symbol outside the scheme's alphabet, or a magnitude past what the scheme's fixed width can represent. Carries a "rule" field naming which of the three fired; never echoes the input.

View Source
const CodeIDTimestampRange errs.Code = 0x00_03_27_07 // 0.3.39.7

CodeIDTimestampRange identifies a generation where the clock sits outside the window the scheme's timestamp field can represent — the KSUID 32-bit second counter, whose epoch starts in 2014 and saturates in 2150. Encoding it anyway would wrap the prefix and destroy the sortability the format exists for.

View Source
const CodeUnknownScheme errs.Code = 0x00_02_07_01 // 0.2.7.1

CodeUnknownScheme identifies a New/Lookup call naming a Scheme that no imported package has registered (a missing blank-import of the scheme).

Variables

View Source
var (
	// UnknownScheme is returned when no Generator is registered under the
	// requested Scheme — typically a missing blank-import of the scheme.
	UnknownScheme = errs.Static(CodeUnknownScheme, "UNKNOWN_SCHEME",
		"No identifier generator is registered under that scheme",
		"core/app/id.New: scheme absent from registry; blank-import the scheme's package to register it",
		0,
		0)

	// DuplicateRegistration is the boot-time panic sentinel for the Generator
	// registry: a nil generator or a distinct generator claiming a taken Scheme.
	DuplicateRegistration = errs.Static(CodeDuplicateRegistration, "DUPLICATE_REGISTRATION",
		"A generator is already registered under that scheme",
		"core/app/id.Register: a distinct generator already claims this scheme, or a nil generator was supplied",
		0,
		0)

	// EntropyFailed was the wrap of a crypto/rand.Read failure while drawing id
	// bytes. Nothing returns it since Go 1.24, whose crypto/rand.Read never
	// fails (a failing source crashes the program); it stays published with its
	// value, so a consumer matching it keeps compiling.
	EntropyFailed = errs.Static(CodeIDEntropyFailed, "ID_ENTROPY_FAILED",
		"Identifier generation failed to read secure random bytes",
		"service/app/id: crypto/rand.Read returned an error",
		0,
		0)

	// ClockBackwards is returned when the snowflake clock regressed past the
	// recoverable sequence window.
	ClockBackwards = errs.Static(CodeIDClockBackwards, "ID_CLOCK_BACKWARDS",
		"Identifier generation observed the clock moving backwards",
		"service/app/id: snowflake clock regressed beyond the same-millisecond sequence space",
		0,
		0)

	// ClockStalled is returned when the snowflake same-millisecond overflow wait
	// gave up because the clock stopped advancing.
	ClockStalled = errs.Static(CodeIDClockStalled, "ID_CLOCK_STALLED",
		"Identifier generation gave up waiting for the clock to advance",
		"service/app/id: snowflake overflow wait exceeded its spin budget without the clock ticking",
		0,
		0)

	// InvalidSize is returned by NewNanoID when the requested identifier length
	// is not positive. Carries a "size" field.
	InvalidSize = errs.Static(CodeIDInvalidSize, "ID_INVALID_SIZE",
		"Identifier generation requires a positive identifier length",
		"service/app/id: NewNanoID received a size <= 0; a zero-length identifier identifies nothing",
		0,
		0)

	// InvalidPrefix is returned by NewTypeID/FormatTypeID/ParseTypeID when the
	// type prefix is empty or breaks the lowercase-ASCII shape. Carries a "rule"
	// field naming the clause that failed, and never echoes the prefix.
	InvalidPrefix = errs.Static(CodeIDInvalidPrefix, "ID_INVALID_PREFIX",
		"Identifier generation requires a valid lowercase type prefix",
		"service/app/id: a TypeID prefix is 1..63 chars of [a-z], with '_' allowed only between two letters",
		0,
		0)

	// Malformed is returned by ParseKSUID/ParseTypeID/FormatTypeID when the
	// input is not a well-formed rendering. Carries a "rule" field naming which
	// check fired (length / alphabet / overflow), never the input itself.
	Malformed = errs.Static(CodeIDMalformed, "ID_MALFORMED",
		"The identifier is not well formed for its scheme",
		"service/app/id: a parse rejected the input's length, alphabet, or magnitude",
		0,
		0)

	// TimestampRange is returned when the clock sits outside the window a
	// scheme's timestamp field can represent (KSUID: 2014-05-13 .. 2150-06-19).
	TimestampRange = errs.Static(CodeIDTimestampRange, "ID_TIMESTAMP_RANGE",
		"Identifier generation observed a clock outside the scheme's timestamp range",
		"service/app/id: the KSUID 32-bit second counter cannot represent this clock reading",
		0,
		0)
)

Functions

func New

func New(scheme Scheme) (newID string, err error)

New generates a fresh identifier using the Generator registered under scheme. A scheme with no registered generator returns UnknownScheme (blank-import the scheme's package to register it); a generator entropy/clock fault propagates unchanged with the scheme's own service-layer code.

Types

type Generator

type Generator interface {
	Scheme() Scheme
	New() (newID string, err error)
}

Generator is the contract every identifier scheme satisfies. Instances MUST be safe for concurrent use (New is called from many goroutines). Scheme- specific options (snowflake node id, etc.) are supplied via the scheme's constructor, not via this interface.

IFACE-PLUGIN: the registry stores plug-in scheme instances behind this interface — concrete scheme types stay unexported per package.

func Lookup

func Lookup(name Scheme) (g Generator, ok bool)

Lookup returns the Generator registered under name.

IFACE-PLUGIN: the registry stores plug-in scheme instances behind the Generator interface — concrete types are intentionally unexported per scheme.

func Register

func Register(g Generator) Generator

Register inserts g under g.Scheme() and returns it so callers can bind the singleton to a typed package-level variable like `var UUIDv4 = id.Register(uuidv4Gen{})`. Panics on a nil generator or when a distinct generator already claims the same Scheme.

IFACE-PLUGIN: the registry hands plug-in Generator instances back to callers so each scheme keeps its concrete type unexported; the contract is the Generator interface itself.

"Nil" here means UNUSABLE, not only an untyped nil: a typed nil pointer and a plug-in whose type is not comparable both satisfy the port and neither can serve one call (see internal/kernel/plugin).

type Scheme

type Scheme string

Scheme is the typed key under which a Generator registers (e.g. "uuidv4", "uuidv7", "ulid", "snowflake", "nanoid", "ksuid"). The zero value Scheme("") is reserved invalid, mirroring codec.Format and transform.Algorithm.

func Available

func Available() []Scheme

Available returns the sorted list of registered Schemes, or nil before any Register.

func (Scheme) Known

func (s Scheme) Known() bool

Known reports whether s has been registered in the Generator registry.

func (Scheme) String

func (s Scheme) String() string

String implements fmt.Stringer and returns the raw identifier.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL