Quick start
Prerequisites
- Docker with the Compose plugin (
docker compose) - A clone of the
qwed-verificationrepository
Dockerfile (the server target) and waits for Postgres, Redis, and Jaeger to report healthy before it starts the API.
Services
The API server keeps its data in the
qwed-data volume. Postgres, Redis, Prometheus, and Grafana each have their own named volume, so data survives docker compose down.
Configuration
Environment variables
deploy/.env.example lists every setting the Compose stack reads. Generate each secret separately:
deploy/.env is git-ignored.
API configuration
Docker
API server image
Theqwedai/qwed-verification image on Docker Hub runs the API server with uvicorn on port 8000. Docker Hub publishes latest from main and a versioned tag for each release from 8.0.0.
The image:
- Runs as a non-root user (UID 1000).
- Uses
/var/lib/qwedas its working directory. The default SQLite database (sqlite:///./qwed.db) lives there. Mount a volume at/var/lib/qwedto keep it, or setDATABASE_URLto use Postgres. - Has a health check that calls
GET /healthwith the image’s own Python. - Reads its bind address and port from
UVICORN_HOST(default0.0.0.0) andUVICORN_PORT(default8000). The health check followsUVICORN_PORT, so change the port with-e UVICORN_PORT=...rather than a--portargument.
Run the published image
Pass the secrets from your shell, so no value is written into the command:QWED_CORS_ORIGINS, API_KEY_SECRET, QWED_JWT_SECRET_KEY, QWED_API_KEY_LOOKUP_SECRET and QWED_AUDIT_SECRET_KEY, and QWED_API_KEY_LOOKUP_SECRET must differ from QWED_JWT_SECRET_KEY. Also set QWED_SIGNING_KEY (an EC P-256 key), or attestations stop verifying after a restart or on another replica.
The server names no model. For requests that use a provider (natural-language, logic, statistics, and fact), also pass the provider’s settings, for example -e ACTIVE_PROVIDER -e OPENAI_API_KEY -e OPENAI_MODEL. The container doesn’t read the qwed init settings on your host. If you keep the values in a .env file instead, pass it with --env-file .env.
Optionally, add a signing key so attestations verify after a restart and on every replica. Without one, each process generates its own key. Mount an unencrypted EC P-256 PEM file and point QWED_SIGNING_KEY_FILE at it by adding these flags to docker run:
Build from source
The repository’sDockerfile has two targets. server is the API server. action is the GitHub Action and stays the default target, so build the server with --target server:
Kubernetes
The repository ships manifests indeploy/kubernetes/. The Deployment pins docker.io/qwedai/qwed-verification:9.1.0. Tags from 8.0.0 contain the API server. Fill in every value in deploy/kubernetes/secret.yaml before you apply it. See Kubernetes deployment.
🏢 Enterprise Support Coming Soon: Managed hosting, dedicated support, and SLA guarantees. Contact support@qwedai.tech
Scaling
Horizontal scaling
- Stateless API servers behind load balancer
- Redis for distributed rate limiting
- PostgreSQL with read replicas
Performance tuning
Availability limits
QWED bounds request size, slow work, and sandbox usage so one request can’t stall or exhaust a server process. The defaults suit most deployments. Raise them only if your host has the headroom./verify JSON route accepts up to 1,000,000 bytes. /verify/stats and /verify/image accept their 10 MB file limit plus envelope headroom. QWED rejects an oversized body with 413 before processing it. See Request limits for the full error list.
QWED_SANDBOX_MAX_CONCURRENT applies per worker process. With uvicorn --workers 4 and the default of 4, a host can run up to 16 sandbox containers. Size the value against your host’s memory, CPU, and PID limits.Sandbox image and container user
v8.0.0
/verify/stats and /verify/consensus run model-generated Python in a Docker sandbox. QWED pins the sandbox image by digest, so the sandbox only ever runs the exact image that was reviewed:
no-new-privileges, and never runs as a privileged root. The only writable path is the per-execution /workspace directory, which is a private 0700 host directory. The container runs as that directory’s owner:
- Non-root server process: the container runs as the host user.
- Root server process: QWED hands each workspace to a fresh uid for that execution. QWED skips any uid that a host account or group owns, that
/etc/subuidor/etc/subgiddelegates, or that another running execution uses. - Rootless daemon run by the same non-root user: the container runs as its root, which the daemon maps to that unprivileged host user.
Monitoring
Health check
Prometheus metrics
GET /metrics/prometheus (the Prometheus text exposition format — /metrics returns JSON). The endpoint requires a platform operator: create a dedicated scraper account, add its user ID to QWED_METRICS_OPERATOR_USER_IDS (fail-closed when unset — every caller is denied, including org owners/admins), mint the account an API key, and send it as a header on the scrape. See All-tenant metrics authorization.
Security
- Always use HTTPS in production
- Set
API_KEY_SECRET— mandatory, no default value - Set
QWED_CORS_ORIGINS— mandatory, explicitly list allowed origins - Set strong API keys
- Enable rate limiting
- Use network isolation
- Rotate secrets regularly
- Configure
QWED_METRICS_OPERATOR_USER_IDSif anything needs all-tenant metrics — unset means deny-all