Vault

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_01h455vb4pex5vsknk084sn02q

The 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() // true

Type 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    = ID

Store 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

ConstantPrefixEntityExample
id.PrefixProviderhpvdProviderhpvd_01h455vb4pex5vsknk084sn02q
id.PrefixTemplatehtplTemplatehtpl_01h455vb4pex5vsknk084sn02q
id.PrefixTemplateVersionhtpvTemplate Versionhtpv_01h455vb4pex5vsknk084sn02q
id.PrefixMessagehmsgMessagehmsg_01h455vb4pex5vsknk084sn02q
id.PrefixInboxhinbInbox Notificationhinb_01h455vb4pex5vsknk084sn02q
id.PrefixPreferencehprfPreferencehprf_01h455vb4pex5vsknk084sn02q
id.PrefixScopedConfighscfScoped Confighscf_01h455vb4pex5vsknk084sn02q

All prefixes start with h to identify Herald entities at a glance.

On this page