TLS and Certificate Management
Cirata Symphony communicates over HTTPS and WSS (secure WebSocket). This guide covers how to configure TLS certificates for your deployment.
Why Reverse Proxy TLS?
Symphony delegates TLS termination to a reverse proxy (such as nginx) rather than handling TLS directly. This is standard practice for production services and provides several advantages:
- Certificate management with standard tools like certbot or cert-manager, independent of Symphony updates
- Load balancing and horizontal scaling behind the proxy
- Unified TLS configuration across all services using well-documented nginx or ingress controller settings
- No additional setup for Docker Compose—the installation bundle includes a pre-configured nginx container that handles TLS automatically
If you are using the Docker Compose deployment, nginx is already bundled in the Docker Compose stack. TLS certificates are generated automatically during setup—no separate reverse proxy configuration is needed.
TLS Architecture
Symphony uses TLS in three places:
- HTTPS—For the UI, REST API, and OAuth flows (port 8080 by default)
- WSS—For WebSocket connections from the browser to the NATS server (port 9222 by default)
- NATS—For native NATS connections from extensions and CLI tools (port 4222)
HTTPS and WSS are typically terminated by a reverse proxy. NATS TLS is handled natively by the embedded NATS server when configured.
Reverse Proxy TLS Termination (Recommended)
nginx
A typical nginx configuration that terminates TLS and proxies to Symphony:
server {
listen 443 ssl;
server_name symphony.example.com;
ssl_certificate /etc/ssl/certs/symphony.crt;
ssl_certificate_key /etc/ssl/private/symphony.key;
ssl_protocols TLSv1.2 TLSv1.3;
# UI and REST API
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# WebSocket for NATS
location /ws {
proxy_pass http://localhost:9222;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
}
Docker Compose with nginx
Add an nginx service to your Docker Compose configuration:
services:
symphony:
image: cirata/symphony:latest
# No need to expose ports externally — nginx proxies to them
volumes:
- symphony-config:/config
nginx:
image: nginx:alpine
ports:
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./certs:/etc/ssl/certs
- ./private:/etc/ssl/private
depends_on:
- symphony
Kubernetes Ingress
For Kubernetes deployments, use an Ingress resource with TLS:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: symphony
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "86400"
nginx.ingress.kubernetes.io/proxy-send-timeout: "86400"
nginx.ingress.kubernetes.io/upstream-hash-by: "$remote_addr"
spec:
tls:
- hosts:
- symphony.example.com
secretName: symphony-tls
rules:
- host: symphony.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: symphony
port:
number: 8080
For WebSocket support, ensure your ingress controller is configured to handle WebSocket upgrades.
NATS TLS (Native Connections)
Extensions and CLI tools that connect to Symphony over the native NATS protocol (port 4222) can use TLS encryption. NATS TLS is configured on the server side; clients automatically detect and use TLS when available.
Enabling NATS TLS on the Server
Provide a TLS certificate and key to Symphony using environment variables:
| Variable | Required | Description |
|---|---|---|
NATS_TLS_CERT | Yes | Path to the PEM-encoded server certificate |
NATS_TLS_KEY | Yes | Path to the PEM-encoded private key |
NATS_TLS_CA | No | Path to a CA certificate for client verification |
Both NATS_TLS_CERT and NATS_TLS_KEY must be set together. The
referenced files must exist and be readable at setup time —
Symphony validates this and will fail to start if the paths are
inaccessible. When configured, the NATS server requires TLS on all
connections—plaintext connections are rejected.
Docker Compose example:
services:
symphony:
image: cirata/symphony:latest
environment:
- NATS_TLS_CERT=/certs/tls.crt
- NATS_TLS_KEY=/certs/tls.key
volumes:
- ./certs:/certs:ro
- symphony-config:/config
Kubernetes (Helm chart):
nats:
tls:
enabled: true
secretName: symphony-nats-tls # K8s TLS secret with tls.crt and tls.key
caSecretName: symphony-nats-ca # Optional: K8s secret with ca.crt
Client Behaviour
All Symphony client libraries (Python, Java, Go, Rust) automatically attempt TLS when connecting over the native NATS protocol. No client-side configuration is required when the server certificate is signed by a publicly trusted CA or one present in the system trust store.
If the server does not have TLS enabled, clients fall back to an unencrypted connection with a warning.
Suppressing TLS Fallback Warnings (CLI)
When connecting to a Symphony instance that intentionally does not
use NATS TLS (for example, in a development environment or when TLS is
terminated at the network layer), the CLI produces warnings on every
invocation. To suppress these warnings, set client.nats_tls = false
in the CLI configuration file:
# ~/.config/cirata/symphony.toml
[client]
nats_tls = false
Or set the CIRATA_NATS_TLS environment variable:
export CIRATA_NATS_TLS=false
This skips the TLS connection attempt entirely, which also avoids the
small delay caused by the failed TLS handshake. When NATS TLS is later
enabled on the server, remove the setting or set it back to true to
resume TLS connections.
Custom CA for Extensions
If the server uses a certificate signed by a private or internal CA,
extensions can specify the CA certificate using the SYMPHONY_TLS_CA
environment variable:
export SYMPHONY_TLS_CA=/path/to/ca.pem
SYMPHONY_TLS_CA applies to both the HTTPS registration exchange
(GET /api/v1/apikey) and the NATS TLS connection. For deployments
that need a different CA for NATS alone, NATS_TLS_CA remains
supported and overrides SYMPHONY_TLS_CA for the NATS connection only.
| Purpose | Resolution |
|---|---|
| HTTPS (registration exchange, client API calls) | SYMPHONY_TLS_CA → system defaults |
| NATS | NATS_TLS_CA → SYMPHONY_TLS_CA → system defaults |
This is only needed when the CA is not in the system trust store. The PEM file may contain a single certificate or a bundle. If the path is set but the file is missing or contains no valid certificates, SDKs fail with a clear error rather than falling back to the system store.
Using Your Own Certificates
Obtaining Certificates
For production deployments, obtain a certificate from a trusted Certificate Authority (CA):
- Let's Encrypt—Free, automated certificates via ACME protocol using certbot
- Commercial CAs—DigiCert, Sectigo, etc.
- Internal CA—For private/corporate deployments
The certificate must cover the hostname used to access Symphony (e.g., symphony.example.com).
Self-Signed Certificates (Development Only)
For development and testing, generate a self-signed certificate using
openssl (pre-installed on most Linux distributions and macOS).
The certificate must include a Subject Alternative Name (SAN)—modern
TLS clients reject certificates that rely only on the legacy Common Name
(CN) field. The output files use the names expected by the bundled nginx
configuration (fullchain.pem and privkey.pem) and are written to the
default certificate directory:
sudo mkdir -p /etc/nginx/certs/symphony
sudo openssl req -x509 -newkey rsa:4096 \
-keyout /etc/nginx/certs/symphony/privkey.pem \
-out /etc/nginx/certs/symphony/fullchain.pem \
-sha256 -days 365 -nodes \
-subj "/CN=symphony.example.com" \
-addext "subjectAltName=DNS:symphony.example.com"
Self-signed certificates will cause browser warnings and are not suitable for production use. Extensions connecting to Symphony may also reject self-signed certificates unless explicitly configured to trust them.
Using the CLI with Self-Signed Certificates
When connecting to a Symphony instance that uses a self-signed certificate, the CLI will reject the connection by default. You have two options:
-
Add the certificate to your system trust store (recommended):
- macOS: Import the certificate into Keychain Access and mark it as trusted
- Linux (Debian/Ubuntu): Copy the certificate to
/usr/local/share/ca-certificates/and runsudo update-ca-certificates - Linux (RHEL/Fedora/SLES): Copy the certificate to
/etc/pki/ca-trust/source/anchors/and runsudo update-ca-trust - Windows: Import the certificate into the Trusted Root Certification Authorities store
-
Skip TLS verification with the
--insecureflag:cirata login --address symphony.example.com --insecure
cirata info --insecureOr set the environment variable to avoid passing the flag on every command:
export CIRATA_TLS_SKIP_VERIFY=true
cirata login --address symphony.example.comOr add it to the CLI configuration file:
# ~/.config/cirata/symphony.toml
[client]
tls_skip_verify = true
--insecure disables all TLS certificate validation. Only use this for
development and testing—never in production.
Certificate Renewal
If using Let's Encrypt with certbot:
# Renew certificates
certbot renew
# Reload nginx to pick up new certificates
nginx -s reload
Automate renewal with a cron job or systemd timer:
# /etc/cron.d/certbot
0 3 * * * root certbot renew --quiet --post-hook "nginx -s reload"
Troubleshooting
Browser Shows Certificate Warning
- Verify the certificate covers the correct hostname
- Check that the certificate chain is complete (includes intermediate certificates)
- Ensure the certificate has not expired:
openssl x509 -enddate -noout -in cert.pem
WebSocket Connection Fails
- Verify the reverse proxy is configured to handle WebSocket upgrades (
UpgradeandConnectionheaders) - Check that the proxy timeout is long enough for persistent connections (
proxy_read_timeout 86400for nginx) - Ensure both port 8080 (HTTP) and 9222 (WS) are proxied
Extensions Cannot Connect
- If using self-signed or private CA certificates, set
SYMPHONY_TLS_CAto the path of the CA certificate (covers both HTTPS and NATS). UseNATS_TLS_CAonly when the NATS CA differs from the HTTPS CA. - Alternatively, add the CA to the system trust store:
- Python: set
SSL_CERT_FILEor usecertifiwith custom CA - Java: add the CA to the JVM truststore
- Go: set
SSL_CERT_FILEor add to system certificate store
- Python: set
- Check Symphony logs for "TLS is enabled" to confirm the server is configured correctly
NATS TLS Not Working
- Verify both
NATS_TLS_CERTandNATS_TLS_KEYare set - Symphony validates at startup that the certificate and key files exist and are readable — check the startup logs for
"NATS TLS file is not accessible" - Ensure the certificate covers the hostname clients use to connect
- Check the NATS server log for
(TLS)afterListening for client connections— its absence means TLS is not active despite configuration - Use
cirata -vto see TLS fallback warnings from the CLI
See Also
- Configuration—Configuration files and directory structure
- OIDC Configuration—Configuring external OIDC providers