Deployment options
| Option | Description | Status |
|---|---|---|
| CydraLabs-managed SaaS | Hosted and operated by CydraLabs. | Pilot programme |
| Customer cloud or private VPC | Deployed into your own cloud account with the CydraLabs reference deployment. | Pilot programme |
| On-premises or sovereign deployment | Planned for environments that cannot use cloud services. | Roadmap |
| Local evaluation (Docker Compose) | The whole platform on one machine with fictional data, for evaluation and development. Not for production. | Evaluation |
Deployment into your own cloud account is delivered through the pilot programme, with CydraLabs engineers. The deployment package (Terraform, Helm chart and container images) is provided to pilot customers; contact us to join.
Components
| Component | Role | Version |
|---|---|---|
| Web | Public site and portal (Next.js); proxies /api to the API so cookies stay on the same site | Included |
| API | REST API, the gateway enforcement point and sign-in (FastAPI, Python 3.12) | Included |
| Worker | Deferred work: risk recalculation, graph projection, webhooks, periodic sweeps (Dramatiq). Exactly one worker runs the scheduler | Included |
| Policy engine | Open Policy Agent with the CydraLabs decision policy | OPA 1.10.1 |
| PostgreSQL | System of record, row-level tenant isolation, graph tables, evidence chain | 16 |
| Redis | Work queue and rate-limit counters (no persistent state) | 7 |
| Object storage | Reports and evidence exports (any S3-compatible store) | S3 API |
| Identity provider | OpenID Connect sign-in for people. Self-service sign-up and mandatory MFA use Keycloak with the CydraLabs extension | Keycloak 26.4 or any OIDC provider |
Analytical services never sit on the action path: a decision needs the API, the database, the policy engine and (for rate limits) Redis. See the architecture.
Docker Compose (evaluation)
For evaluation and development, not production: it runs in development mode with development credentials, a development sign-in that needs no password, seeded fictional organisations and no TLS.
docker compose up --build
# optional OpenTelemetry collector:
docker compose --profile observability up --build
# reset all data:
docker compose down -v| Service | URL |
|---|---|
| Portal | http://localhost:3000 |
| API and Swagger UI | http://localhost:8000/api/docs |
| Keycloak (realm cydralabs) | http://localhost:8080 |
| Mailpit (captured email) | http://localhost:8025 |
The stack also runs PostgreSQL, Redis, OPA, SeaweedFS (S3-compatible storage), a one-off migration job and the worker. Sign in by choosing one of the seeded users of Northwind Financial Group or Contoso Health. On Windows, keep the checkout outside folders synchronised by OneDrive.
AWS reference deployment
Terraform for a production-shaped environment in your AWS account, in any region (examples use eu-west-1):
| Area | What is created |
|---|---|
| Network | VPC across 2 or 3 availability zones with public, private and isolated data subnets, NAT gateways, an S3 gateway endpoint and flow logs |
| Compute | Amazon EKS 1.33 with a managed Graviton node group (m7g.large, 3 to 9 nodes), Kubernetes secrets encrypted with KMS, private API endpoint by default |
| Database | Amazon RDS for PostgreSQL 16, Multi-AZ, encrypted with KMS, TLS required, 14-day backups, deletion protection |
| Cache | Amazon ElastiCache for Redis 7, Multi-AZ with automatic failover, encryption at rest and in transit, AUTH token |
| Storage | S3 bucket for exports: versioning, Object Lock (governance mode, 365 days by default), SSE-KMS, a bucket policy that refuses requests without TLS |
| Secrets | AWS Secrets Manager for database and Redis URLs, signing key seed, pepper and the OIDC client secret |
| Images | Amazon ECR repositories with immutable tags and scan on push |
| Edge | Application Load Balancer (TLS 1.3/1.2 policy), ACM certificate, AWS WAF with managed rule groups and a per-IP rate limit, Route 53 record |
| Deploy access | Optional GitHub Actions OIDC role, so deployments need no stored AWS keys |
Deploying
- Prerequisites: Terraform 1.9 or later, AWS CLI v2, kubectl, Helm 3, Docker with buildx, and an OIDC identity provider (Entra ID, Okta, Cognito or Keycloak) with the redirect URI
https://<your host>/api/v1/auth/callback. - Copy the example variables file, set the environment, region, public URL, DNS zone and OIDC settings, then
terraform init,planandapply(about 25 to 35 minutes). - Store the OIDC client secret in the Secrets Manager entry Terraform created.
- Run the deploy script. It builds arm64 images (with SBOM and provenance), installs the AWS Load Balancer Controller, creates the database roles, writes the Kubernetes secrets, installs the Helm chart with an atomic upgrade and runs smoke tests.
Kubernetes Helm chart
Chart cydralabs 0.1.0 (Kubernetes 1.27 or later). Databases, Redis and object storage are always external; the chart never contains secret values.
helm upgrade --install cydra ./cydralabs -n cydralabs --create-namespace \
-f ./cydralabs/values-production.yaml| Workload | Defaults |
|---|---|
| api | 2 replicas, autoscaling 2–8 (production values 3–12), disruption budget, probes on /healthz and /readyz |
| web | 2 replicas, autoscaling 2–6 (production 3–10), probe on /healthz |
| worker | 1 replica (production 2), plus a single worker-scheduler |
| opa | 2 replicas (production 3), reachable from the API and worker and nothing else |
| Migration job | Pre-install and pre-upgrade hook running database migrations with the owner credentials |
| Ingress | nginx by default; ALB in the production values; /api routed straight to the API |
- Secrets:
cydralabs-app(DATABASE_URL,REDIS_URL,SIGNING_KEY_SEED,SECRET_PEPPER,OIDC_CLIENT_SECRET, optionalSIGNING_PREVIOUS_KEYSand S3 keys) andcydralabs-migrate(DATABASE_OWNER_URL). With IAM roles for service accounts, omit the S3 keys. - Hardening: non-root (UID 10001), immutable root filesystem (readOnlyRootFilesystem), all capabilities dropped, no privilege escalation, RuntimeDefault seccomp, no service-account token mounted.
- Network policies: default deny, DNS allowed, per-component ingress, and egress limited to PostgreSQL, Redis, object storage and the identity provider.
- Images: pin by digest in production; a digest takes precedence over the tag.
Production settings
All settings are environment variables, validated at start-up. With ENVIRONMENT=production, the API, worker and migration job refuse to start when any of these is true:
- Development sign-in is enabled (
AUTH_DEV_LOGIN_ENABLED). SIGNING_KEY_SEEDorSECRET_PEPPERstill has its development value.SESSION_COOKIE_SECUREis not true.CORS_ALLOWED_ORIGINScontains*.POLICY_FAIL_OPEN_READONLYis set withoutPOLICY_FAIL_OPEN_ACKNOWLEDGED=true.OIDC_ISSUER,OIDC_CLIENT_IDorOIDC_CLIENT_SECRETis missing.EMAIL_BACKEND=smtpwithoutSMTP_HOST,SMTP_USERNAMEandSMTP_PASSWORD, or withSMTP_STARTTLSoff; or thememoryemail backend, which is meant for tests.- Self-service sign-up (
AUTH_SIGNUP_ENABLED) withoutKEYCLOAK_SIGNUP_CLIENT_SECRETor without SMTP email. AUTH_EVENTS_SECRETis set but shorter than 32 characters.
| Setting | Recommendation |
|---|---|
DATABASE_URL | The runtime role (cydra_app), never the schema owner; TLS required |
REDIS_URL | TLS (rediss://) with an AUTH token |
POLICY_ENGINE / OPA_URL | opa and the OPA service URL (the default is the embedded evaluator) |
OBJECT_STORE / S3_* | s3, the bucket and region. If the bucket requires KMS, set S3_SERVER_SIDE_ENCRYPTION=aws:kms and S3_KMS_KEY_ID |
PUBLIC_WEB_URL / OIDC_REDIRECT_URI | Your public host and its /api/v1/auth/callback |
WORKER_ENABLED | true, with the worker deployed |
TRUST_FORWARDED_HEADERS | true behind a load balancer, with FORWARDED_ALLOW_IPS limited to it |
OTEL_EXPORTER_OTLP_ENDPOINT | Your OpenTelemetry collector, for traces |
RETENTION_*_DAYS | Retention periods to match your policy |
Database and migrations
- Create two roles before running any migration:
cydra_owner, which owns the schema, andcydra_app(LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOBYPASSRLS), which the application uses. The deploy script and the Compose stack do this for you. - Migrations run as the owner (
alembic upgrade head; the Helm hook or the Composemigratejob). The application must never connect as the owner: row-level security relies on the runtime role. - Do not load demonstration data in production. Your organisation is created when its administrator signs in and completes onboarding.
Operating the platform
Health
GET /healthz reports that the process is up; GET /readyz also checks the database. The web app has its own /healthz.
Backups and restore
- PostgreSQL holds all authoritative state, including the evidence chain: use point-in-time recovery and test restores regularly.
- After a restore, verify the evidence chain (
GET /api/v1/evidence/verify, or theverify-evidencecommand) and rebuild the graph (POST /api/v1/graph/rebuild). - Redis needs no backup.
Upgrades
Migrations run before the new version starts. Schema changes are made in expand, migrate and contract steps so the previous version keeps working during a rolling upgrade. Roll back a release with helm rollback; check the release notes before rolling back across a migration.
Observability
Logs are structured JSON on standard output, with secrets and personal data redacted. Traces are exported over OTLP/HTTP (API requests and database calls; health checks excluded). Metrics and logs are not exported over OTLP in this release.
Applies to the CydraLabs proof-of-concept platform. Last updated 4 October 2026. Questions or corrections: contact us.