Deterministic UUID Generation for Cross-System Coordination
📚 Read the full documentation | Getting Started | API Reference | CLI Guide
UUID-Forge provides a simple, secure way to generate deterministic UUIDs that remain consistent across multiple storage systems without requiring inter-service communication or centralized ID generation.
When building microservices or distributed systems, you often need the same entity to have the same ID across multiple storage systems:
- Postgres (primary database)
- S3 (document storage)
- Redis (caching)
- QDrant (vector database)
- MinIO (object storage)
Traditional approaches require:
- ❌ Central ID generation service (single point of failure)
- ❌ Database lookups before accessing storage (performance impact)
- ❌ Storing mappings between systems (complexity)
- ❌ Service-to-service communication (latency)
UUID-Forge generates deterministic UUIDs from your business data:
from uuid_forge import generate_uuid_only, IDConfig
import os
config = IDConfig(salt=os.getenv("UUID_FORGE_SALT"))
# Generate UUID from business data
invoice_uuid = generate_uuid_only(
"invoice",
config=config,
region="EUR",
number=12345
)
# Later, regenerate the EXACT SAME UUID from the same data
# No database lookup needed!
regenerated = generate_uuid_only(
"invoice",
config=config,
region="EUR",
number=12345
)
assert invoice_uuid == regenerated # ✓ Always True!Core Principle: Same Input + Same Config = Same UUID, Every Time
- 🔒 Secure: Uses cryptographic salt to prevent UUID prediction
- 🎯 Deterministic: Same inputs always produce the same UUID
- 🚀 Zero Coordination: No service communication required
- 📦 Simple API: Functional-first with optional OO convenience
- 🔧 Production Ready: Type-safe, well-tested, documented
- 🎨 CLI Included: First-class command-line interface
- 🐍 Modern Python: Requires Python 3.11+
# With uv (recommended)
uv add uuid-forge
# With pip
pip install uuid-forge
# With all extras
pip install uuid-forge[dev,docs]Comprehensive documentation is available at darth-veitcher.github.io/uuid-forge
- Getting Started - Installation and first steps
- Configuration Guide - Environment setup and security
- Basic Usage - Core concepts and patterns
- Advanced Usage - Complex scenarios and optimization
- CLI Guide - Command-line interface reference
- API Reference - Complete API documentation
- Use Cases - Real-world examples and patterns
# Generate a secure salt
uuid-forge new-salt
# Or initialize a config file
uuid-forge initAdd the generated salt to your environment:
export UUID_FORGE_SALT='your-generated-salt-here'UUID-Forge offers both functional and object-oriented approaches:
from uuid_forge import generate_uuid_only, load_config_from_env
# Load config from environment
config = load_config_from_env()
# Generate deterministic UUID
user_uuid = generate_uuid_only(
"user",
config=config,
email="[email protected]"
)from uuid_forge import UUIDGenerator, IDConfig
import os
# Create a generator with your configuration
generator = UUIDGenerator(
config=IDConfig(salt=os.getenv("UUID_FORGE_SALT"))
)
# Generate multiple UUIDs with same config (no repetitive config passing)
user_uuid = generator.generate("user", email="[email protected]")
invoice_uuid = generator.generate("invoice", number=12345, region="EUR")
order_uuid = generator.generate("order", user_id=str(user_uuid), total=99.99)
# Generate with human-readable prefixes
prefixed_id = generator.generate_with_prefix(
"user",
prefix="USER",
email="[email protected]"
)
# Result: "USER-550e8400-e29b-41d4-a716-446655440000"
# Perfect for service classes - encapsulates configuration
class InvoiceService:
def __init__(self, salt: str):
self.uuid_gen = UUIDGenerator(config=IDConfig(salt=salt))
def create_invoice_id(self, region: str, number: int) -> str:
return self.uuid_gen.generate_with_prefix(
"invoice",
prefix=f"INV-{region}",
region=region,
number=number
)# Postgres - UUID as primary key
db.execute(
"INSERT INTO users (id, email) VALUES (%s, %s)",
(user_uuid, "[email protected]")
)
# S3 - UUID in object key
s3.put_object(
Bucket="users",
Key=f"profiles/{user_uuid}.json",
Body=profile_data
)
# Redis - UUID in cache key
redis.set(f"user:{user_uuid}", user_data, ex=3600)
# Later, regenerate UUID from business data - no lookup needed!
uuid_from_data = generate_uuid_only(
"user",
config=config,
email="[email protected]"
)
# All systems now accessible with the same UUIDfrom uuid_forge import UUIDGenerator, IDConfig, UUID, Namespace
from typing import Protocol
import os
class EntityRepository(Protocol):
def generate_id(self, **kwargs) -> UUID: ...
class InvoiceRepository:
def __init__(self):
self.uuid_generator = UUIDGenerator(
config=IDConfig(
namespace=Namespace("invoices.mycompany.com"),
salt=os.getenv("UUID_FORGE_SALT")
)
)
def generate_id(self, region: str, number: int) -> UUID:
return self.uuid_generator.generate("invoice", region=region, number=number)
def generate_prefixed_id(self, region: str, number: int) -> str:
return self.uuid_generator.generate_with_prefix(
"invoice",
prefix=f"INV-{region}",
region=region,
number=number
)from uuid_forge import UUIDGenerator, IDConfig, UUID
from enum import Enum
class EntityType(Enum):
USER = "user"
ORDER = "order"
PRODUCT = "product"
INVOICE = "invoice"
class UUIDFactory:
def __init__(self, config: IDConfig):
self.generators = {
EntityType.USER: UUIDGenerator(config),
EntityType.ORDER: UUIDGenerator(config),
EntityType.PRODUCT: UUIDGenerator(config),
EntityType.INVOICE: UUIDGenerator(config),
}
def create_uuid(self, entity_type: EntityType, **attributes) -> UUID:
return self.generators[entity_type].generate(entity_type.value, **attributes)
def create_prefixed_uuid(self, entity_type: EntityType, **attributes) -> str:
prefix_map = {
EntityType.USER: "USR",
EntityType.ORDER: "ORD",
EntityType.PRODUCT: "PRD",
EntityType.INVOICE: "INV",
}
return self.generators[entity_type].generate_with_prefix(
entity_type.value,
prefix=prefix_map[entity_type],
**attributes
)
# Usage
factory = UUIDFactory(config=load_config_from_env())
user_uuid = factory.create_uuid(EntityType.USER, email="[email protected]")
order_id = factory.create_prefixed_uuid(EntityType.ORDER, user_id=user_uuid, items=["A", "B"])from abc import ABC, abstractmethod
from uuid_forge import UUIDGenerator, IDConfig, UUID
class UUIDService(ABC):
@abstractmethod
def generate_user_uuid(self, email: str) -> UUID: ...
@abstractmethod
def generate_order_uuid(self, user_id: UUID, timestamp: int) -> UUID: ...
class ProductionUUIDService(UUIDService):
def __init__(self, config: IDConfig):
self.generator = UUIDGenerator(config)
def generate_user_uuid(self, email: str) -> UUID:
return self.generator.generate("user", email=email)
def generate_order_uuid(self, user_id: UUID, timestamp: int) -> UUID:
return self.generator.generate("order", user_id=str(user_id), timestamp=timestamp)
class TestUUIDService(UUIDService):
def __init__(self):
# Use deterministic config for testing
self.generator = UUIDGenerator(
config=IDConfig(salt="test-salt-for-reproducible-tests")
)
def generate_user_uuid(self, email: str) -> UUID:
return self.generator.generate("user", email=email)
def generate_order_uuid(self, user_id: UUID, timestamp: int) -> UUID:
return self.generator.generate("order", user_id=str(user_id), timestamp=timestamp)- Microservices Architecture: Multiple services need consistent IDs
- Multi-Storage Systems: Postgres + S3 + Redis + QDrant + MinIO
- Zero-Coordination Design: No central ID service required
- Deterministic Testing: Reproducible IDs for test scenarios
- Data Migration: Consistent IDs across old and new systems
- Service-Oriented Architecture: Clean dependency injection patterns
- Multi-Tenant Applications: Isolated UUID namespaces per tenant
- Simple CRUD Apps: Use database auto-increment
- Sequential IDs Required: Use database sequences
- No Salt Available: UUIDs become predictable (security risk)
- High-Performance Scenarios: UUID generation has computational overhead
UUID-Forge provides flexible configuration through the IDConfig class:
from uuid_forge import IDConfig, Namespace
import os
# Default configuration (no salt - not recommended for production)
config = IDConfig()
# Production configuration with salt
config = IDConfig(salt=os.getenv("UUID_FORGE_SALT"))
# Custom namespace for your organization
config = IDConfig(
namespace=Namespace("mycompany.com"),
salt=os.getenv("UUID_FORGE_SALT")
)from uuid_forge import load_config_from_env
# Load from default environment variables
config = load_config_from_env()
# Reads: UUID_FORGE_NAMESPACE and UUID_FORGE_SALT
# Load from custom environment variables
config = load_config_from_env(
namespace_env="MY_UUID_NAMESPACE",
salt_env="MY_UUID_SALT"
)-
Namespace: Provides logical separation between applications
- Default:
uuid.NAMESPACE_DNS - Custom:
Namespace("your-domain.com") - From env: Set
UUID_FORGE_NAMESPACE=your-domain.com
- Default:
-
Salt: Adds cryptographic security to prevent UUID prediction
- Default:
""(empty - not secure for production) - Generated: Use
uuid-forge new-saltcommand - From env: Set
UUID_FORGE_SALT=your-generated-salt
- Default:
from uuid_forge import UUIDGenerator, IDConfig, UUID, Namespace
import os
class UserService:
def __init__(self):
self.uuid_generator = UUIDGenerator(
config=IDConfig(
namespace=Namespace("users.mycompany.com"),
salt=os.getenv("UUID_FORGE_SALT")
)
)
def create_user_uuid(self, email: str) -> UUID:
return self.uuid_generator.generate("user", email=email)from uuid_forge import UUIDGenerator, IDConfig, UUID, Namespace
import os
class TenantUUIDService:
def __init__(self, tenant_id: str):
self.generator = UUIDGenerator(
config=IDConfig(
namespace=Namespace(f"tenant-{tenant_id}.mycompany.com"),
salt=os.getenv("UUID_FORGE_SALT")
)
)
def generate_entity_uuid(self, entity_type: str, **kwargs) -> UUID:
return self.generator.generate(entity_type, **kwargs)CRITICAL: Always use a salt in production!
# ❌ INSECURE - UUIDs are predictable
config = IDConfig()
# ✅ SECURE - UUIDs are unpredictable
config = IDConfig(salt=os.getenv("UUID_FORGE_SALT"))Generate a secure salt:
uuid-forge new-saltStore it securely:
- Environment variables
- Secret management systems (AWS Secrets Manager, HashiCorp Vault, etc.)
- Never commit to version control
Full documentation is available at: https://darth-veitcher.github.io/uuid-forge
from uuid_forge import generate_uuid_only, generate_uuid_with_prefix, extract_uuid_from_prefixed
# Generate UUID
uuid_obj = generate_uuid_only("user", config=config, email="[email protected]")
# Generate with prefix
prefixed_id = generate_uuid_with_prefix("user", config=config, prefix="USR", email="[email protected]")
# Extract UUID from prefixed ID
uuid_obj = extract_uuid_from_prefixed("USR-550e8400-e29b-41d4-a716-446655440000")from uuid_forge import UUIDGenerator, IDConfig
# Create generator
generator = UUIDGenerator(config=IDConfig(salt="your-salt"))
# Generate UUIDs
uuid_obj = generator.generate("user", email="[email protected]")
prefixed_id = generator.generate_with_prefix("user", prefix="USR", email="[email protected]")from uuid_forge import IDConfig, load_config_from_env, generate_salt
# Create configurations
config = IDConfig(salt="your-salt")
config = load_config_from_env()
# Generate secure salt
salt = generate_salt() # 32 bytes by default
salt = generate_salt(64) # Custom lengthUUID-Forge includes a comprehensive CLI:
# Generate UUID
uuid-forge generate invoice --attr region=EUR --attr number=12345
# With human-readable prefix
uuid-forge generate invoice --prefix INV-EUR --attr region=EUR --attr number=12345
# Extract UUID from prefixed ID
uuid-forge extract "INV-EUR-550e8400-e29b-41d4-a716-446655440000"
# Generate new salt
uuid-forge new-salt
# Initialize config file
uuid-forge init
# Validate configuration
uuid-forge validate
# Show current config
uuid-forge infoUUID-Forge uses UUIDv5 (name-based, SHA-1) for deterministic generation:
- Entity Type provides logical separation ("invoice", "user", "order")
- Business Data uniquely identifies the entity (region, number, email, etc.)
- Salt adds security (prevents UUID prediction)
- Namespace provides additional isolation (optional)
The combination is hashed to produce a UUID that's:
- ✅ Deterministic (same inputs → same UUID)
- ✅ Unique (different inputs → different UUIDs)
- ✅ Secure (unpredictable with salt)
- ✅ Standard (RFC 4122 compliant)
# Clone repository
git clone https://github.com/darth-veitcher/uuid-forge.git
cd uuid-forge
# Install with uv (includes all dev dependencies)
uv sync --all-groups
# Run tests
uv run pytest
# Run tests with coverage
uv run pytest --cov=uuid_forge --cov-report=html
# Run linting and formatting
uv run ruff check src tests
uv run ruff format src tests
uv run mypy src
# Run all checks with nox
uv run nox
# Build the package
uv build
# Build and serve documentation
uv run mkdocs serve- Lines of Code: ~300 (core), ~1000 (with tests)
- Test Coverage: >80%
- Type Coverage: 100%
- Dependencies: Minimal (typer, rich for CLI)
Contributions are welcome! Please see CONTRIBUTING.md for details.
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Ensure tests pass and coverage remains >80%
- Submit a pull request
MIT License - see LICENSE for details.
For detailed documentation, tutorials, and examples, visit:
🌐 https://darth-veitcher.github.io/uuid-forge
The documentation includes:
- Comprehensive guides and tutorials
- Real-world use case examples
- Complete API reference
- CLI command reference
- Best practices and security guidelines
- Migration guides and troubleshooting
- Inspired by the need for zero-coordination microservices
- Built with modern Python best practices using
uvandhatch-vcs - Follows PEP-8, uses strict typing, and comprehensive testing
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Repository: https://github.com/darth-veitcher/uuid-forge
Made with ❤️ for developers who value simplicity and determinism