domain-key
High-performance, type-safe, domain-driven key system for Rust applications
Never mix up keys from different domains again!
What is domain-key?
domain-key brings Domain-Driven Design principles to key management in Rust. It provides compile-time guarantees that keys from different business domains cannot be accidentally mixed or compared, while delivering exceptional performance through advanced optimizations.
use ;
// Define your business domains
;
;
// Create domain-specific key types
type UserKey = ;
type OrderKey = ;
Key Features
- Type Safety: Different key types cannot be mixed at compile time
- High Performance: Up to 75% performance improvements through advanced optimizations
- Domain Agnostic: No built-in assumptions about specific domains
- Memory Efficient: Smart string handling with stack allocation for short keys
- DoS Resistant: Optional protection against HashDoS attacks
- Extensible: Easy to add new domains and validation rules
- Zero-Cost Abstractions: No runtime overhead for type separation
- Cross-Platform: Works on all major platforms including WebAssembly
- Compile-Time Validation: Catch invalid key literals at compile time, not at runtime
Quick Start
Add to your Cargo.toml:
[]
# Recommended for most projects (DoS-resistant hashing)
= { = "0.4", = ["secure"] }
# Maximum performance (requires AES-NI capable CPU)
= { = "0.4", = ["fast"] }
# Bare minimum (standard hasher, no extra deps)
= "0.4"
Define a domain and create keys:
use ;
// 1. Define your domain
;
// 2. Create a type alias
type UserKey = ;
// 3. Use it!
let user_key = new?;
let composed_key = from_parts?;
println!;
println!; // O(1) with optimizations
println!;
# Ok::
// Or use macros for less boilerplate:
use ;
define_domain!;
key_type!;
// Compile-time validation — invalid literals are compile errors, not panics
const _: = assert!;
Identifier Types
domain-key provides three typed identifier wrappers:
| Type | Storage | Use case |
|---|---|---|
Key<D> |
SmartString |
Human-readable keys with validation |
Id<D> |
NonZeroU64 |
Numeric database IDs (8 bytes, Copy) |
Uuid<D> |
uuid::Uuid |
UUID identifiers (16 bytes, Copy, feature uuid) |
use *;
// Numeric IDs — one macro does it all
define_id!;
let id = new.unwrap;
assert_eq!;
// Or define domain and alias separately
define_id_domain!;
id_type!;
let order = new.unwrap;
assert_eq!;
// UUID identifiers (requires `uuid` feature)
use *;
define_uuid!;
let uuid = nil;
assert_eq!;
// With v4 random generation (requires `uuid-v4` feature)
// Prefer the unified `new()` API:
// let uuid = OrderUuid::new();
All three types are domain-typed: UserId and OrderId are incompatible at compile time even though both wrap a NonZeroU64.
Performance Features
Feature-Based Optimization Profiles
# Maximum performance (modern CPUs with AES-NI)
= ["fast"]
# DoS protection + good performance
= ["secure"]
# Cryptographic security
= ["crypto"]
# All optimizations enabled
= ["fast", "std", "serde"]
Build for Maximum Performance
# Enable CPU-specific optimizations
RUSTFLAGS="-C target-cpu=native"
# For Apple Silicon Macs
RUSTFLAGS="-C target-cpu=native -C target-feature=+aes,+neon"
Performance Improvements
| Operation | Standard | Optimized | Improvement |
|---|---|---|---|
| Key Creation (short) | 100ns | 72ns | 28% faster |
| String Operations | 100% baseline | 175% | 75% faster |
| Struct Size | 40 bytes (old) | 32 bytes | Compact, cache-line friendly |
| HashMap Lookup | by Key (alloc) | by &str |
zero-alloc via Borrow<str> |
| Collection Lookup | 35ns | 21ns | 40% faster |
Advanced Examples
E-commerce Domain
use ;
;
;
type ProductKey = ;
type CartKey = ;
// Use in your application
let product = new?;
let cart = from_parts?;
# Ok::
Multi-tenant SaaS
use ;
use Cow;
;
type TenantKey = ;
let tenant = new?;
assert_eq!; // normalized
# Ok::
Advanced Validation
use ;
;
type EmailKey = ;
let email = new?;
assert_eq!;
// This will fail validation
let invalid = new;
assert!;
# Ok::
Error variants:
KeyParseErrorincludes bothTooLong { max_length, actual_length }andTooShort { min_length, actual_length }variants (new in v0.4). Keys shorter thanT::min_length()now produce a dedicated, pattern-matchableTooShorterror instead of the genericInvalidStructure. Exhaustivematchexpressions onKeyParseErrormust handle both arms.
Static Keys
Create static keys where invalid literals are compile errors (not runtime panics) since v0.4.2:
use ;
define_domain!;
type UserKey = ;
// Invalid literals are compile errors (not runtime panics) since v0.4.2
let key = static_key!;
assert_eq!;
Macros
The define_domain!, key_type!, define_id!, define_id_domain!, id_type!, define_uuid!, and related macros all accept an optional leading visibility specifier ($vis:vis). This means you can control the visibility of the generated types just like any other Rust item:
use ;
define_domain!; // public
key_type!; // crate-visible
define_domain!; // private (module-local)
Compile-Time Key Validation
Production code often creates keys from known-good string literals — yet the idiomatic
Key::new("value").expect("valid") hides a panic path that only triggers at runtime.
Version 0.4.2 eliminates that problem entirely.
The tools
| Tool | Where | What it checks |
|---|---|---|
is_valid_key_default(s, max) |
free const fn (crate root) |
default rules against any length |
Key::<T>::is_valid_key_const(s) |
inherent const fn on Key<T> |
default rules with T::MAX_LENGTH |
MyDomain::is_valid_key(s) |
generated by define_domain! |
default rules with this domain's MAX_LENGTH |
static_key!(T, "lit") |
macro | default rules at compile time + custom rules at runtime |
All four work in const contexts — the compiler evaluates them before your binary runs.
Before / After
Before — hidden panic paths scattered through the codebase:
let admin = new.expect;
let health = new.expect;
let default = new.expect;
After — compile errors for invalid literals, zero runtime panics:
let admin = static_key!;
let health = static_key!;
let default = static_key!;
// Invalid literals are caught by the compiler, not at runtime:
// let bad = static_key!(UserKey, "bad key!"); // ← compile error
Document invariants next to your domain
use ;
define_domain!;
type UserKey = ;
// These const assertions compile to zero bytes — they are pure proof obligations.
// If MAX_LENGTH or the rules change, the assertions break the build immediately.
const _: = assert!;
const _: = assert!;
const _: = assert!;
const _: = assert!;
const _: = assert!;
Low-level const fn
For build.rs, procedural macros, or any context where you need raw validation without a
domain type:
use ;
const
const _: = assert!;
Note:
is_valid_key_defaultandKey::is_valid_key_constcheck only the defaultKeyDomainrules. Custom rules added viavalidate_domain_rulesare enforced at runtime byKey::new/Key::try_from_static.
Feature Flags Reference
Hash Algorithm Features (choose one for best results)
fast- GxHash (40% faster, requires modern CPU with AES-NI)secure- AHash (DoS protection, balanced performance)crypto- Blake3 (cryptographically secure)- Default - Standard hasher (good compatibility)
Note: Without any hash feature enabled, the library falls back to a basic FNV-1a hasher which is not DoS-resistant and is slower than ahash. For most applications, we recommend enabling the
securefeature.
Identifier Features
uuid- Typed UUID identifiers (Uuid<D>)uuid-v4- UUID v4 random generation (Uuid::new(),Uuid::v4()deprecated)uuid-v7- UUID v7 time-ordered generation (Uuid::now_v7())
Core Features
std- Standard library support (enabled by default)serde- Serialization support (enabled by default)no_std- No standard library support
Security Considerations
domain-key provides multiple levels of security depending on your needs:
- DoS Protection: Use
securefeature for AHash with DoS resistance - Cryptographic Security: Use
cryptofeature for Blake3 cryptographic hashing - Input Validation: Comprehensive validation pipeline with custom rules
- Type Safety: Compile-time prevention of key type mixing
- Memory Safety: Rust's ownership system + additional optimizations
See SECURITY.md for detailed security information.
Documentation
- User Guide - Comprehensive usage guide
- API Documentation - Complete API reference
- Examples - Real-world usage examples
- Migration Guide - Migrating from string keys
- Performance Guide - Optimization strategies
- Security Policy - Security considerations and reporting
Testing
Run the comprehensive test suite:
# Run tests with recommended features (hash features are mutually exclusive —
# passing --all-features triggers a compile_error)
# Property-based tests
# Benchmarks
# Security audit
Benchmarks
# Run realistic benchmarks
# Memory usage analysis
# Cross-platform performance
Migration from String Keys
Before (String-based)
let user_id: String = "user_123".to_string;
let order_id: String = "order_456".to_string;
// Dangerous - no compile-time protection!
if user_id == order_id
let cache_key = format!;
After (domain-key)
type UserKey = ;
type OrderKey = ;
type CacheKey = ;
let user_id = new?;
let order_id = new?;
// This won't compile - type safety!
// if user_id == order_id { } // Compile error!
let cache_key = from_parts?;
# Ok::
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Quick Development Setup
# Install development dependencies
# Run tests (hash features are mutually exclusive; use explicit feature list)
Platform Support
| Platform | Status | Hash Algorithm | Notes |
|---|---|---|---|
| Linux x86_64 | Full | GxHash/AHash | Best performance with AES-NI |
| Windows x86_64 | Full | GxHash/AHash | Full feature support |
| macOS Intel | Full | GxHash/AHash | All features supported |
| macOS Apple Silicon | Full | GxHash/AHash | Requires explicit AES+NEON flags |
| WebAssembly | Core | DefaultHasher | no_std support |
| ARM64 Linux | Full | GxHash/AHash | Server deployments |
| ARM Embedded | Core | FNV-1a | no_std + no_alloc |
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Inspired by Domain-Driven Design principles by Eric Evans
- Built on the excellent
smartstringcrate for memory efficiency - Performance-focused hash algorithms from the Rust ecosystem:
Project Stats
- Test Coverage: >95%
- Documentation Coverage: >98%
- Benchmark Coverage: 20+ realistic scenarios
- no_std Support: Yes
- MSRV: Rust 1.86+
- Platforms: 7+ supported targets
domain-key - Because your keys should know their place in your domain!