Self-Hosted Deployment
Self-Hosted Deployment¶
Deploy Huitzo on your own infrastructure with full control over your data. Target deployment time: <30 minutes.
Prerequisites¶
See Requirements for detailed hardware specifications, platform compatibility, and scaling guidelines.
Quick reference: - 4+ cores, 8+ GB RAM, 50+ GB SSD recommended - Docker 28.0+ and Docker Compose 2.28+ required
Platform Compatibility¶
Huitzo supports multiple architectures via multi-arch Docker images:
| Platform | Architecture | Supported | Notes |
|---|---|---|---|
| Linux | x86_64 (amd64) | ✅ | Primary development platform |
| Linux | arm64 (aarch64) | ✅ | AWS Graviton, Oracle Ampere |
| macOS | Intel (x86_64) | ✅ | Docker Desktop |
| macOS | Apple Silicon (arm64) | ✅ | Docker Desktop, native performance |
Software Requirements¶
- Docker 28.0+ (29.x recommended)
- Docker Compose v2.28+
- Linux (Ubuntu 24.04 LTS recommended) or macOS
ARM Architecture Notes¶
Huitzo Docker images are built for both amd64 and arm64 architectures:
# Images automatically select correct architecture
docker pull huitzo/huitzo:latest
# Verify architecture
docker inspect huitzo/huitzo:latest | grep Architecture
ARM-specific considerations:
- Performance: Native ARM images run efficiently on ARM hosts (no emulation)
- Dependencies: All Python dependencies have ARM wheels available
- PostgreSQL/Redis: Official images support ARM natively
- Build from source: If building custom images, use multi-arch builds:
# Build multi-arch image
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t huitzo/huitzo:latest \
--push .
Self-hosted on ARM:
# docker-compose.yml works unchanged on ARM
# Images auto-select correct architecture
services:
app:
image: huitzo/huitzo:latest # Works on both amd64 and arm64
# ... rest of configuration
License Key¶
You'll need a valid Huitzo license key. Contact [email protected] or visit huitzo.com/pricing.
Quick Start¶
1. Download Configuration¶
# Create deployment directory
mkdir huitzo && cd huitzo
# Download docker-compose.yml and configuration
curl -sSL https://get.huitzo.com/docker-compose.yml -o docker-compose.yml
curl -sSL https://get.huitzo.com/.env.example -o .env
2. Configure Environment¶
Edit .env with your settings:
# .env
# License
HUITZO_LICENSE_KEY=your-license-key-here
# Security (generate a random key)
HUITZO_SECRET_KEY=your-random-secret-key-change-me
# Database
POSTGRES_PASSWORD=your-secure-database-password
# Optional: Email (for notifications)
SENDGRID_API_KEY=SG.xxxxx
HUITZO_EMAIL_FROM=[email protected]
# Optional: LLM providers
OPENAI_API_KEY=sk-xxxxx
ANTHROPIC_API_KEY=sk-ant-xxxxx
Generate a secure secret key:
openssl rand -hex 32
3. Start Services¶
docker compose up -d
4. Verify Installation¶
# Check all services are running
docker compose ps
# Check health endpoint
curl http://localhost:8080/health
Expected output:
{
"status": "healthy",
"version": "2.0.0",
"services": {
"database": "healthy",
"redis": "healthy",
"worker": "healthy"
}
}
5. Access Huitzo¶
- WebCLI: http://localhost:8080
- API Docs: http://localhost:8080/docs
- Health: http://localhost:8080/health
Docker Compose Configuration¶
Full docker-compose.yml¶
services:
app:
image: huitzo/huitzo:latest
restart: unless-stopped
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgresql://huitzo:${POSTGRES_PASSWORD}@postgres:5432/huitzo
- REDIS_URL=redis://redis:6379/0
- SECRET_KEY=${HUITZO_SECRET_KEY}
- LICENSE_KEY=${HUITZO_LICENSE_KEY}
- LOG_LEVEL=${LOG_LEVEL:-INFO}
# Optional integrations
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
- SENDGRID_API_KEY=${SENDGRID_API_KEY:-}
- EMAIL_FROM=${HUITZO_EMAIL_FROM:-}
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
volumes:
- ./packs:/app/packs:ro
- ./data/uploads:/app/uploads
worker:
image: huitzo/huitzo:latest
command: ["celery", "-A", "huitzo.worker", "worker", "-l", "info"]
restart: unless-stopped
environment:
- DATABASE_URL=postgresql://huitzo:${POSTGRES_PASSWORD}@postgres:5432/huitzo
- REDIS_URL=redis://redis:6379/0
- SECRET_KEY=${HUITZO_SECRET_KEY}
- LICENSE_KEY=${HUITZO_LICENSE_KEY}
depends_on:
- app
- redis
deploy:
resources:
limits:
memory: 512M
postgres:
image: postgres:17-alpine
restart: unless-stopped
environment:
- POSTGRES_USER=huitzo
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=huitzo
volumes:
- ./data/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U huitzo"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:8-alpine
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
- ./data/redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres_data:
redis_data:
Resource Allocation (Containerized Services)¶
| Service | Memory | CPU | Purpose |
|---|---|---|---|
| app | 1.5 GB | 1.5 cores | API server, WebCLI |
| worker | 512 MB | 0.5 cores | Background tasks |
| postgres | 1 GB | 0.75 cores | Data storage |
| redis | 256 MB | 0.25 cores | Cache, task queue |
Using External Services¶
PostgreSQL and Redis do not need to be containers. You can use:
- Cloud-managed services: AWS RDS, Azure Database for PostgreSQL, Google Cloud SQL, Amazon ElastiCache, Azure Cache for Redis, etc.
- On-premises servers: Dedicated PostgreSQL/Redis servers in your datacenter
- Existing infrastructure: Connect to databases you already manage
External Services Configuration¶
When using external services, provide connection strings via environment variables:
# .env for external services
DATABASE_URL=postgresql://user:[email protected]:5432/huitzo
REDIS_URL=redis://:[email protected]:6379/0
# With SSL (recommended for cloud services)
DATABASE_URL=postgresql://user:[email protected]:5432/huitzo?sslmode=require
REDIS_URL=rediss://:[email protected]:6379/0
docker-compose for External Services¶
When using external PostgreSQL and Redis, use a simplified docker-compose:
# docker-compose.yml (external services)
services:
app:
image: huitzo/huitzo:latest
restart: unless-stopped
ports:
- "8080:8080"
environment:
- HUITZO_DEPLOYMENT_MODE=self_hosted
# External database (no container dependency)
- DATABASE_URL=${DATABASE_URL}
# External Redis (no container dependency)
- REDIS_URL=${REDIS_URL}
- SECRET_KEY=${HUITZO_SECRET_KEY}
- LICENSE_KEY=${HUITZO_LICENSE_KEY}
- LOG_LEVEL=${LOG_LEVEL:-INFO}
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
volumes:
- ./packs:/app/packs:ro
- ./data/uploads:/app/uploads
worker:
image: huitzo/huitzo:latest
command: ["celery", "-A", "huitzo.worker", "worker", "-l", "info", "-Q", "default,fast,medium,long"]
restart: unless-stopped
environment:
- HUITZO_DEPLOYMENT_MODE=self_hosted
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- SECRET_KEY=${HUITZO_SECRET_KEY}
- LICENSE_KEY=${HUITZO_LICENSE_KEY}
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
volumes:
- ./packs:/app/packs:ro
- ./data/uploads:/app/uploads
beat:
image: huitzo/huitzo:latest
command: ["celery", "-A", "huitzo.worker", "beat", "-l", "info"]
restart: unless-stopped
environment:
- HUITZO_DEPLOYMENT_MODE=self_hosted
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- SECRET_KEY=${HUITZO_SECRET_KEY}
- LICENSE_KEY=${HUITZO_LICENSE_KEY}
Note: No depends_on for postgres/redis since they're external services.
Cloud Provider Examples¶
AWS RDS + ElastiCache:
# .env
DATABASE_URL=postgresql://huitzo:[email protected]:5432/huitzo?sslmode=require
REDIS_URL=rediss://huitzo-cache.abc123.cache.amazonaws.com:6379/0
Azure Database + Cache:
# .env
DATABASE_URL=postgresql://huitzo@huitzo-server:[email protected]:5432/huitzo?sslmode=require
REDIS_URL=rediss://:[email protected]:6380/0
Google Cloud SQL + Memorystore:
# .env
DATABASE_URL=postgresql://huitzo:MySecurePass@/huitzo?host=/cloudsql/project:region:instance
REDIS_URL=redis://10.0.0.5:6379/0
Database Requirements¶
When using external PostgreSQL:
| Requirement | Value |
|---|---|
| Version | PostgreSQL 14+ (17 recommended) |
| Extensions | uuid-ossp, pgcrypto (usually pre-installed) |
| User permissions | CREATE, SELECT, INSERT, UPDATE, DELETE on database |
| SSL | Recommended for cloud connections |
When using external Redis:
| Requirement | Value |
|---|---|
| Version | Redis 6+ (8 recommended) |
| Mode | Standalone or Sentinel (Cluster not yet supported) |
| Persistence | AOF recommended for task queue durability |
| Memory | 256 MB minimum, 1 GB recommended |
Verifying External Connections¶
Before starting Huitzo, verify connectivity:
# Test PostgreSQL connection
psql "${DATABASE_URL}" -c "SELECT version();"
# Test Redis connection
redis-cli -u "${REDIS_URL}" ping
File Storage Configuration¶
User uploads and pack output files require storage. Options:
| Backend | Best For | Configuration |
|---|---|---|
| Local filesystem | Single-instance, dev/test | Default, volume mount |
| S3-compatible | Production, multi-instance | AWS S3, MinIO, Spaces |
| Azure Blob | Azure deployments | Native integration |
| Google Cloud Storage | GCP deployments | Native integration |
Local Filesystem (Default)¶
The default configuration stores files on the local filesystem via Docker volume:
# docker-compose.yml (default)
services:
app:
volumes:
- ./data/uploads:/app/uploads
Limitation: Does not work for multi-instance deployments (files not shared between containers).
S3-Compatible Storage (Production)¶
For production and multi-instance deployments, use S3-compatible storage.
AWS S3:
# .env
HUITZO_FILE_STORAGE_BACKEND=s3
HUITZO_S3_BUCKET=mycompany-huitzo-uploads
HUITZO_S3_REGION=us-east-1
HUITZO_S3_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
HUITZO_S3_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
MinIO (Self-Hosted S3-Compatible):
Add MinIO to your docker-compose.yml:
# docker-compose.yml
services:
minio:
image: minio/minio:latest
command: server /data --console-address ":9001"
restart: unless-stopped
ports:
- "9000:9000" # S3 API
- "9001:9001" # Console
environment:
- MINIO_ROOT_USER=${MINIO_ACCESS_KEY:-minioadmin}
- MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY:-minioadmin}
volumes:
- ./data/minio:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 10s
retries: 3
Configure Huitzo to use MinIO:
# .env
HUITZO_FILE_STORAGE_BACKEND=s3
HUITZO_S3_BUCKET=uploads
HUITZO_S3_ENDPOINT=http://minio:9000
HUITZO_S3_ACCESS_KEY_ID=minioadmin
HUITZO_S3_SECRET_ACCESS_KEY=minioadmin
HUITZO_S3_USE_SSL=false
HUITZO_S3_PATH_STYLE=true
Create the bucket after starting MinIO:
# Using MinIO client
docker compose exec minio mc alias set local http://localhost:9000 minioadmin minioadmin
docker compose exec minio mc mb local/uploads
Azure Blob Storage¶
# .env
HUITZO_FILE_STORAGE_BACKEND=azure
HUITZO_AZURE_STORAGE_ACCOUNT=mycompanystorage
HUITZO_AZURE_STORAGE_KEY=base64encodedkey==
HUITZO_AZURE_CONTAINER=huitzo-uploads
Google Cloud Storage¶
# .env
HUITZO_FILE_STORAGE_BACKEND=gcs
HUITZO_GCS_BUCKET=mycompany-huitzo-uploads
HUITZO_GCS_PROJECT=my-gcp-project
HUITZO_GCS_CREDENTIALS_FILE=/secrets/gcs-credentials.json
Mount the credentials file:
# docker-compose.yml
services:
app:
volumes:
- ./secrets/gcs-credentials.json:/secrets/gcs-credentials.json:ro
When to Use Each Backend¶
| Deployment Scenario | Recommended Backend |
|---|---|
| Single instance, dev/test | Local filesystem |
| Single instance, production | Local or S3 (your choice) |
| Multi-instance / HA | S3-compatible (required) |
| AWS infrastructure | AWS S3 |
| Azure infrastructure | Azure Blob |
| GCP infrastructure | Google Cloud Storage |
| Air-gapped / on-premises | MinIO |
See File Storage Backends for detailed configuration and migration guides.
HTTP Security for External Integrations¶
Packs that integrate with external APIs (e.g., Maxerience, Stripe) declare allowed domains in their manifests.
Configuring Global Trusted Domains¶
For internal services not declared by packs:
# docker-compose.yml
environment:
HUITZO_HTTP_GLOBAL_ALLOWED_DOMAINS: "api.internal.corp,*.partner.com"
Security Model¶
- Pack declares
allowed_domainsinhuitzo.yaml - Platform enforces domain restrictions at runtime
- Requests to unlisted domains are blocked with
HTTPSecurityError - Global allowlist supplements (not overrides) pack declarations
See Configuration Reference for all HTTP options.
Installing Packs¶
From Files¶
Place pack files in the ./packs directory:
huitzo/
├── docker-compose.yml
├── .env
└── packs/
├── my-pack-1.0.0.tar.gz
└── another-pack-2.0.0.tar.gz
Restart to load packs:
docker compose restart app worker
From Registry¶
# Using the CLI inside the container
docker compose exec app huitzo pack install my-pack
# Or via API
curl -X POST http://localhost:8080/api/v1/packs/install \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{"name": "my-pack", "version": "1.0.0"}'
Configuration Reference¶
Deployment Mode¶
Self-hosted deployments use the same Docker image as Huitzo Cloud, with behavior controlled by the HUITZO_DEPLOYMENT_MODE parameter:
environment:
- HUITZO_DEPLOYMENT_MODE=self_hosted
| Mode | Description |
|---|---|
cloud |
Multi-tenant SaaS (Huitzo-managed) |
self_hosted |
Single-tenant, customer-managed infrastructure |
edge |
Offline-capable edge devices (Year 3+) |
The codebase is modular—self-hosted activates license validation and single-tenant logic while excluding cloud-specific code paths (billing, multi-tenant isolation). See Build Architecture for details.
Environment Variables¶
| Variable | Required | Default | Description |
|---|---|---|---|
HUITZO_DEPLOYMENT_MODE |
✅ | cloud |
Must be self_hosted for self-hosted deployments |
HUITZO_LICENSE_KEY |
✅ | - | License key from Huitzo |
HUITZO_SECRET_KEY |
✅ | - | Secret key for JWT signing |
HUITZO_DEFAULT_TENANT_ID |
- | Auto-generated | Fixed tenant ID for self-hosted |
POSTGRES_PASSWORD |
✅ | - | PostgreSQL password |
DATABASE_URL |
- | Auto | Full database connection URL |
REDIS_URL |
- | Auto | Full Redis connection URL |
LOG_LEVEL |
- | INFO |
Logging level |
OPENAI_API_KEY |
- | - | OpenAI API key |
ANTHROPIC_API_KEY |
- | - | Anthropic API key |
SENDGRID_API_KEY |
- | - | SendGrid API key |
Volume Mounts¶
| Path | Purpose |
|---|---|
./data/postgres |
PostgreSQL data (persistent) |
./data/redis |
Redis data (persistent) |
./data/uploads |
User uploaded files |
./packs |
Intelligence packs |
Operations¶
Viewing Logs¶
# All services
docker compose logs -f
# Specific service
docker compose logs -f app
docker compose logs -f worker
Stopping Services¶
# Stop all
docker compose down
# Stop and remove volumes (⚠️ DATA LOSS)
docker compose down -v
Updating¶
# Pull latest images
docker compose pull
# Restart with new images
docker compose up -d
Backup¶
# Backup PostgreSQL
docker compose exec postgres pg_dump -U huitzo huitzo > backup.sql
# Backup all data
tar -czvf huitzo-backup-$(date +%Y%m%d).tar.gz ./data
Restore¶
# Restore PostgreSQL
cat backup.sql | docker compose exec -T postgres psql -U huitzo huitzo
# Restore all data
tar -xzvf huitzo-backup-20240101.tar.gz
docker compose restart
Production Recommendations¶
1. Use HTTPS¶
Put Huitzo behind a reverse proxy with TLS:
# /etc/nginx/sites-available/huitzo
server {
listen 443 ssl http2;
server_name huitzo.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/huitzo.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/huitzo.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
2. Configure Backups¶
Add automated daily backups:
# /etc/cron.daily/huitzo-backup
#!/bin/bash
cd /opt/huitzo
./backup.sh
3. Monitor Health¶
Set up monitoring with your preferred tool:
# Health check endpoint
curl -f http://localhost:8080/health || alert "Huitzo is down"
4. Log Rotation¶
Configure Docker log rotation in /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
Troubleshooting¶
Container Won't Start¶
# Check logs
docker compose logs app
# Common issues:
# - Invalid license key
# - Database connection failed
# - Missing environment variables
Database Connection Failed¶
# Check PostgreSQL is running
docker compose ps postgres
# Check connectivity
docker compose exec app pg_isready -h postgres -U huitzo
License Validation Failed¶
- Verify your license key is correct in
.env - Check internet connectivity (for online validation)
- Contact [email protected] if issues persist
Worker Not Processing Tasks¶
# Check worker logs
docker compose logs worker
# Restart worker
docker compose restart worker
# Check Redis connectivity
docker compose exec worker redis-cli -h redis ping
Licensing¶
Self-hosted Huitzo requires a valid license key. Licenses are validated through keygen.sh integration.
License Validation Flow¶
┌─────────────────────────────────────────────────────────────────┐
│ Startup │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Online Check │ │
│ │ (keygen.sh) │ │
│ └────────┬────────┘ │
│ │ │
│ ┌─────────────┼─────────────┐ │
│ │ Success │ Failure │ │
│ ▼ ▼ │ │
│ ┌──────────┐ ┌──────────────┐ │ │
│ │ Cache │ │ Use Cached │ │ │
│ │ Response │ │ Validation │ │ │
│ │ (7 days) │ │ (if exists) │ │ │
│ └──────────┘ └──────────────┘ │ │
│ │ │ │ │
│ ▼ ▼ │ │
│ ┌──────────────────────────────┐ │ │
│ │ Normal Operation │ │ │
│ └──────────────────────────────┘ │ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Cache Expired & │ │
│ │ No Connection? │ │
│ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Read-Only Mode │ │
│ │ (No new commands)│ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
License Authentication¶
Huitzo uses license key authentication for self-hosted deployments:
# .env - Only one credential needed
HUITZO_LICENSE_KEY=ABC-123-DEF-456-789
How it works: 1. Customer receives license key from Huitzo 2. Backend validates license with Keygen.sh using license key authentication 3. Backend activates machines (hardware binding) using the same license key 4. All operations are scoped to that license only
Security Model:
| Deployment | Authentication Method | Scope |
|---|---|---|
| Self-Hosted | License Key (License <key>) |
Single license only |
| Cloud | Product Token (Bearer <token>) |
Huitzo-managed (internal) |
Important: Self-hosted deployments do NOT use product tokens. Product tokens grant full account management access and must remain Huitzo-side only. License key authentication follows Keygen best practices and provides the correct security boundaries for customer deployments.
License States¶
| State | Description | Behavior |
|---|---|---|
| Valid | License verified online | Full functionality |
| Cached | Offline, using cached validation | Full functionality (7-day TTL) |
| Grace | Cached validation expired | Read-only mode, warning displayed |
| Invalid | License revoked or expired | Platform refuses to start |
Offline Grace Period¶
Self-hosted deployments can operate offline for up to 7 days using cached license validation:
# Environment variables for offline mode
HUITZO_LICENSE_KEY=your-license-key
HUITZO_OFFLINE_GRACE_DAYS=7 # Default: 7 days
During the grace period: - All commands continue to work - New packs can be installed - Warning logs indicate offline status
After grace period expires: - New command executions are blocked - Existing data remains accessible - Platform logs error and suggests connectivity check
License Environment Variables¶
| Variable | Required | Default | Description |
|---|---|---|---|
HUITZO_LICENSE_KEY |
✅ | - | Your license key |
HUITZO_LICENSE_VALIDATION_URL |
❌ | https://api.keygen.sh |
Validation endpoint |
HUITZO_OFFLINE_GRACE_DAYS |
❌ | 7 |
Offline grace period |
HUITZO_LICENSE_CHECK_INTERVAL |
❌ | 86400 |
Re-validation interval (seconds) |
Troubleshooting License Issues¶
License validation failed:
# Check license key format
echo $HUITZO_LICENSE_KEY | head -c 10
# Test connectivity to keygen.sh
curl -I https://api.keygen.sh/v1/ping
# Check container logs
docker compose logs app | grep -i license
Stuck in read-only mode:
# Force license re-validation
docker compose exec app huitzo license validate
# Clear cached validation (will require online check)
docker compose exec app huitzo license clear-cache
SaaS vs Self-Hosted Licensing¶
| Aspect | SaaS | Self-Hosted |
|---|---|---|
| Billing | Stripe subscription | Annual/monthly license fee |
| Validation | Automatic (Stripe webhooks) | keygen.sh API |
| Offline | Not applicable | 7-day grace period |
| Usage Limits | Subscription tier | None (customer manages infra) |
Testing¶
Running License Validation Integration Tests¶
Integration tests for the licensing subsystem live at:
apps/backend/tests/integration/test_licensing_e2e.py
The tests use respx to intercept outbound HTTP requests, so they run without any live credentials by default:
# Run all mocked integration tests
uv run pytest apps/backend/tests/integration/test_licensing_e2e.py -v
# Run only a specific scenario group
uv run pytest apps/backend/tests/integration/test_licensing_e2e.py \
-k "TestValidationFlow" -v
Test scenario groups¶
| Class | Covers |
|---|---|
TestValidationFlow |
All keygen response codes (VALID, SUSPENDED, EXPIRED, OVERDUE, BANNED, NO_MACHINE) |
TestStartupValidation |
Missing key, valid key, invalid key at boot time |
TestMiddlewareResponses |
402 on INVALID, X-License-Warning on GRACE, public-path bypass |
TestCachePersistence |
Cache write/read, restart survival, corruption recovery, grace timing |
TestNetworkFailureScenarios |
Offline with/without cache, HTTP timeout, 5xx fallback |
TestKeygenSandboxE2E |
Live keygen.sh sandbox (skipped without credentials) |
Live Sandbox Tests (optional)¶
To run the live TestKeygenSandboxE2E tests you need a keygen.sh sandbox
account and the following environment variables:
export KEYGEN_SANDBOX_ACCOUNT_ID="your-sandbox-account-id"
export KEYGEN_SANDBOX_PRODUCT_ID="your-sandbox-product-id"
export KEYGEN_SANDBOX_PRODUCT_TOKEN="your-sandbox-product-token"
export KEYGEN_SANDBOX_VALID_LICENSE_KEY="a-valid-key-in-your-sandbox"
export KEYGEN_SANDBOX_SUSPENDED_LICENSE_KEY="a-suspended-key" # optional
uv run pytest apps/backend/tests/integration/test_licensing_e2e.py \
-k "TestKeygenSandboxE2E" -v
CI – Automated License Integration Tests¶
A dedicated CI workflow runs these tests automatically:
- Scheduled: every Monday at 04:00 UTC
- On push: when licensing code changes land on
main - Manual: via the GitHub Actions "License Integration Tests" workflow
Configure the following repository secrets to enable live sandbox tests in CI:
| Secret | Description |
|---|---|
KEYGEN_SANDBOX_ACCOUNT_ID |
Sandbox account ID |
KEYGEN_SANDBOX_PRODUCT_ID |
Sandbox product ID |
KEYGEN_SANDBOX_PRODUCT_TOKEN |
Sandbox product token |
KEYGEN_SANDBOX_VALID_LICENSE_KEY |
A valid license key |
KEYGEN_SANDBOX_SUSPENDED_LICENSE_KEY |
A suspended license key (optional) |
Next Steps¶
- Requirements – Detailed hardware and network requirements
- Operations – Day-2 operations guide
- Observability – Monitoring and metrics
- Configuration Reference – All environment variables
- Architecture Overview – Deployment modes and system design
- API Reference – API documentation