Identity (TypeID)
How Chronicle uses type-safe, prefix-qualified identifiers for every entity.
Every entity in Chronicle 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're looking at.
A TypeID looks like this:
audit_01j9vk2qz3p4r5t6w7x8y9z0ab
stream_01j9vk2qz3p4r5t6w7x8y9z0ab
erasure_01j9vk2qz3p4r5t6w7x8y9z0abPrefixes
Chronicle defines seven entity prefixes:
| Prefix | Entity | Constructor |
|---|---|---|
audit_ | Audit event | id.NewAuditID() |
stream_ | Hash chain stream | id.NewStreamID() |
erasure_ | GDPR erasure record | id.NewErasureID() |
report_ | Compliance report | id.NewReportID() |
retpol_ | Retention policy | id.NewPolicyID() |
archive_ | Retention archive | id.NewArchiveID() |
plugin_ | Plugin registration | id.New(id.PrefixPlugin) |
The id.ID struct
All entity types use the same id.ID struct with a valid flag. A zero-value id.ID represents a nil / unset identifier. You always construct IDs through the constructors or parsers — never by initialising the struct directly.
import "github.com/xraph/chronicle/id"
// Create new IDs
auditID := id.NewAuditID()
streamID := id.NewStreamID()
// Parse from string (any prefix)
parsed, err := id.ParseAny("audit_01j9vk...")
// Parse with prefix validation (returns error if wrong prefix)
auditID, err := id.ParseAuditID("audit_01j9vk...")
streamID, err := id.ParseStreamID("stream_01j9vk...")
// Check for nil
if auditID.IsNil() { ... }
// Format as string
s := auditID.String() // "audit_01j9vk..."Passing an erasure_ string to id.ParseAuditID returns an error — prefix validation is type-safe at parse time.
Serialization
id.ID implements all standard interfaces out of the box:
| Interface | Representation |
|---|---|
encoding.TextMarshaler / TextUnmarshaler | "audit_01j9vk..." |
json.Marshaler / json.Unmarshaler | "audit_01j9vk..." |
driver.Valuer (database write) | "audit_01j9vk..." as string |
sql.Scanner (database read) | accepts string or []byte |
Nil IDs marshal as "" in JSON and as NULL in SQL.
Why TypeIDs?
- K-sortable — UUIDv7 embeds a millisecond timestamp, so IDs naturally sort by creation time in database indexes.
- Human-readable — the prefix tells you at a glance what kind of record you're looking at in logs, URLs, and database queries.
- Type safety —
ParseAuditIDvsParseStreamIDcatches mismatched IDs at parse time rather than at query time. - Compact — base32-encoded UUIDs are shorter than UUID hex strings.