Identity (TypeID)
How Herald uses prefix-qualified, globally unique identifiers for every entity.
Every entity in Herald has a TypeID. TypeIDs are globally unique, K-sortable, URL-safe identifiers built on UUIDv7 with a human-readable prefix that tells you what kind of entity you are looking at.
A TypeID looks like this:
hpvd_01h455vb4pex5vsknk084sn02qThe hpvd prefix identifies this as a Provider. The suffix is a base32-encoded UUIDv7 that encodes creation time, so IDs sort chronologically.
The id package
Import: github.com/xraph/herald/id
The id package wraps the TypeID Go library (v2) with a single ID struct. All entity types share the same struct -- the prefix distinguishes them.
Creating IDs
Use the generic id.New constructor with a prefix constant, or the convenience constructors for each entity type:
import "github.com/xraph/herald/id"
// Generic constructor
providerID := id.New(id.PrefixProvider) // hpvd_01h455vb...
templateID := id.New(id.PrefixTemplate) // htpl_01h455vb...
// Convenience constructors (preferred)
providerID := id.NewProviderID() // hpvd_...
templateID := id.NewTemplateID() // htpl_...
versionID := id.NewTemplateVersionID() // htpv_...
messageID := id.NewMessageID() // hmsg_...
inboxID := id.NewInboxID() // hinb_...
preferenceID := id.NewPreferenceID() // hprf_...
scopedCfgID := id.NewScopedConfigID() // hscf_...Parsing IDs
Parse a raw string into an ID. Use ParseWithPrefix or the convenience parsers to validate the prefix at parse time:
// Parse any Herald ID (no prefix validation).
parsed, err := id.Parse("hpvd_01h455vb4pex5vsknk084sn02q")
// Parse with explicit prefix validation.
parsed, err := id.ParseWithPrefix("hpvd_01h455vb...", id.PrefixProvider)
// Convenience parsers (validate prefix automatically).
providerID, err := id.ParseProviderID("hpvd_01h455vb...")
templateID, err := id.ParseTemplateID("htpl_01h455vb...")
versionID, err := id.ParseTemplateVersionID("htpv_01h455vb...")
messageID, err := id.ParseMessageID("hmsg_01h455vb...")
inboxID, err := id.ParseInboxID("hinb_01h455vb...")
prefID, err := id.ParsePreferenceID("hprf_01h455vb...")
scopeID, err := id.ParseScopedConfigID("hscf_01h455vb...")
// Parse without type checking (alias for id.Parse).
anyID, err := id.ParseAny("hmsg_01h455vb...")If the string is empty, malformed, or has the wrong prefix, parsing returns an error.
MustParse
For hardcoded IDs in tests or seed data, MustParse panics on error instead of returning one:
known := id.MustParse("hpvd_01h455vb4pex5vsknk084sn02q")Nil ID
The zero-value ID is called Nil. It represents an absent or unset identifier:
var empty id.ID
empty.IsNil() // true
empty.String() // ""
id.Nil.IsNil() // trueType aliases
Each entity type has a named alias for readability. These are type aliases (=), not distinct types, so they are fully interchangeable with id.ID:
type ProviderID = ID
type TemplateID = ID
type TemplateVersionID = ID
type MessageID = ID
type InboxID = ID
type PreferenceID = ID
type ScopedConfigID = IDStore interfaces and struct fields use these aliases to document intent:
type Provider struct {
ID id.ProviderID `json:"id"`
// ...
}Database storage
id.ID implements database/sql.Scanner and database/sql/driver.Valuer. IDs are stored as plain strings (TEXT / VARCHAR) in all databases. A Nil ID stores as SQL NULL.
// Writing to the database (driver.Valuer).
providerID.Value() // ("hpvd_01h455vb...", nil)
id.Nil.Value() // (nil, nil) -- SQL NULL
// Reading from the database (sql.Scanner).
var scanned id.ID
row.Scan(&scanned)JSON serialization
id.ID implements encoding.TextMarshaler and encoding.TextUnmarshaler. Nil IDs serialize as empty strings:
{ "id": "hpvd_01h455vb4pex5vsknk084sn02q" }{ "id": "" }BSON serialization
For MongoDB, id.ID implements bson.ValueMarshaler and bson.ValueUnmarshaler (mongo-driver v2). IDs are stored as BSON strings. Nil IDs store as BSON null.
// MarshalBSONValue returns bsonTypeString with the ID as a UTF-8 string.
// UnmarshalBSONValue reads a BSON string and parses it back into an ID.Prefix reference
| Constant | Prefix | Entity | Example |
|---|---|---|---|
id.PrefixProvider | hpvd | Provider | hpvd_01h455vb4pex5vsknk084sn02q |
id.PrefixTemplate | htpl | Template | htpl_01h455vb4pex5vsknk084sn02q |
id.PrefixTemplateVersion | htpv | Template Version | htpv_01h455vb4pex5vsknk084sn02q |
id.PrefixMessage | hmsg | Message | hmsg_01h455vb4pex5vsknk084sn02q |
id.PrefixInbox | hinb | Inbox Notification | hinb_01h455vb4pex5vsknk084sn02q |
id.PrefixPreference | hprf | Preference | hprf_01h455vb4pex5vsknk084sn02q |
id.PrefixScopedConfig | hscf | Scoped Config | hscf_01h455vb4pex5vsknk084sn02q |
All prefixes start with h to identify Herald entities at a glance.