rust-ksuid
A pure-Rust, fully tested implementation of KSUIDs — K-sortable, globally unique identifiers compatible with Segment's original KSUID spec.
Repository Health
Technical Analysis
svix-ksuid is a small, dependency-light Rust crate for generating and working with KSUIDs (K-Sortable Unique IDs) — 20-byte identifiers that embed a timestamp alongside random payload bytes, so IDs sort roughly by creation time even without a central coordinator. It is byte-for-byte compatible with Segment’s original Go implementation, so IDs generated by either library interoperate freely across services written in different languages.
The crate exposes two variants behind a single KsuidLike trait: the standard Ksuid type with one-second timestamp accuracy, and KsuidMs, which trades one byte of random payload for roughly 4ms accuracy while remaining a valid, interchangeable KSUID. Both support base62 string encoding/decoding, raw byte access, and total ordering out of the box, and the crate is generic over the timestamp type — pulling in time, chrono, or jiff as optional feature flags, or falling back to std::time::SystemTime with zero extra dependencies. An optional serde feature adds transparent serialize/deserialize support.
What You Get
Ksuid— the standard KSUID variant with one-second timestamp accuracy, matching the original Segment KSUID spec exactlyKsuidMs— a millisecond-accuracy (~4ms) variant that trades one payload byte for finer time ordering, while still producing valid, interoperable KSUIDs- Base62 encode/decode —
to_base62()/from_base62()(andDisplay/FromStrimpls) for the standard 27-character string representation - Pluggable timestamp backends — feature flags for
time(0.3.x),chrono(0.4.x), andjiff(0.2.x), or plainstd::time::SystemTimewith no extra dependencies - Optional serde support —
Serialize/Deserializeimpls for bothKsuidandKsuidMsgated behind theserdefeature - Total ordering —
PartialOrd/Ordderive directly from the byte layout, so KSUIDs can be sorted or compared like any other value
Common Use Cases
- Database primary keys that need to be both globally unique and roughly time-sortable without a central sequence generator
- Distributed event or log IDs where approximate chronological ordering across services simplifies debugging and pagination
- Interop with existing Segment-KSUID-based systems (e.g. services already using the Go
segmentio/ksuidlibrary) - Request/webhook IDs in systems (like Svix’s own webhook delivery service) that need lexically sortable identifiers exposed to API consumers
- Any UUID-replacement use case where sort order by creation time is a nice-to-have property
Under The Hood
Architecture
The crate centers on a single KsuidLike trait implemented by two concrete 20-byte tuple structs, Ksuid and KsuidMs, defined in one file (src/lib.rs). The trait supplies shared behavior — base62 encode/decode, byte access, payload slicing, Display/FromStr — while each struct implements only the timestamp-specific pieces: Ksuid packs a 4-byte big-endian seconds-since-KSUID-epoch prefix, KsuidMs packs a 5-byte prefix combining seconds and a compressed 4ms-resolution sub-second component. A separate Timestamp trait abstracts over the pluggable time backends (SystemTime, time::OffsetDateTime, chrono::DateTime<Utc>, jiff::Timestamp), each implemented behind its own cfg-gated feature block, with a DefaultTimestamp type alias resolving to whichever backend is enabled. This keeps the core ID logic decoupled from any specific time library — changing the default timestamp resolution order only touches the type-alias cfg block, nothing else.
Tech Stack
Pure Rust, edition 2024, minimum Rust version 1.88. Core dependencies are minimal: byteorder for big-endian integer packing, base-encode for base62 string conversion, and getrandom for payload randomness. Optional dependencies (time, chrono, jiff, serde) are all gated behind Cargo features so consumers only pay for what they use; time03 is enabled by default for backward compatibility. Build tooling is a plain cargo workflow plus a justfile for common tasks, with deny.toml configured for cargo-deny dependency auditing and a pre-commit config for formatting/linting.
Code Quality
The crate ships with an extensive unit test suite inline in src/lib.rs covering timestamp round-tripping across every optional backend (SystemTime, time, chrono, jiff), plus a separate tests/integration.rs file that validates behavior against a fixture file of known KSUID test vectors (tests/test_kuids.txt) for cross-implementation compatibility. Error handling uses a small custom Error type implementing std::error::Error rather than panics for recoverable failures like malformed base62 input, though a few internal byte-copy paths do .unwrap() on invariants the type system already guarantees. CI (.github/workflows) and .pre-commit-config.yaml enforce formatting and linting on every change; typos.toml and deny.toml add spelling and dependency-policy checks.
API Design
The public API is deliberately small and consistent: both ID types expose the same now(), new(), from_seconds(), from_bytes(), to_base62()/from_base62() surface via the shared trait, so switching between Ksuid and KsuidMs requires no code changes beyond the type name. Doctests embedded throughout src/lib.rs double as both documentation and executable examples, and the crate’s default feature set (time03) means use svix_ksuid::*; Ksuid::now(None) works with zero configuration for the common case, while advanced users can opt into chrono04/jiff02/serde explicitly.