Skip to main content

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
info

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:

  1. HTTPS—For the UI, REST API, and OAuth flows (port 8080 by default)
  2. WSS—For WebSocket connections from the browser to the NATS server (port 9222 by default)
  3. 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.

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:

VariableRequiredDescription
NATS_TLS_CERTYesPath to the PEM-encoded server certificate
NATS_TLS_KEYYesPath to the PEM-encoded private key
NATS_TLS_CANoPath 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.

PurposeResolution
HTTPS (registration exchange, client API calls)SYMPHONY_TLS_CA → system defaults
NATSNATS_TLS_CASYMPHONY_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"
warning

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:

  1. 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 run sudo update-ca-certificates
    • Linux (RHEL/Fedora/SLES): Copy the certificate to /etc/pki/ca-trust/source/anchors/ and run sudo update-ca-trust
    • Windows: Import the certificate into the Trusted Root Certification Authorities store
  2. Skip TLS verification with the --insecure flag:

    cirata login --address symphony.example.com --insecure
    cirata info --insecure

    Or set the environment variable to avoid passing the flag on every command:

    export CIRATA_TLS_SKIP_VERIFY=true
    cirata login --address symphony.example.com

    Or add it to the CLI configuration file:

    # ~/.config/cirata/symphony.toml
    [client]
    tls_skip_verify = true
caution

--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 (Upgrade and Connection headers)
  • Check that the proxy timeout is long enough for persistent connections (proxy_read_timeout 86400 for 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_CA to the path of the CA certificate (covers both HTTPS and NATS). Use NATS_TLS_CA only when the NATS CA differs from the HTTPS CA.
  • Alternatively, add the CA to the system trust store:
    • Python: set SSL_CERT_FILE or use certifi with custom CA
    • Java: add the CA to the JVM truststore
    • Go: set SSL_CERT_FILE or add to system certificate store
  • Check Symphony logs for "TLS is enabled" to confirm the server is configured correctly

NATS TLS Not Working

  • Verify both NATS_TLS_CERT and NATS_TLS_KEY are 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) after Listening for client connections — its absence means TLS is not active despite configuration
  • Use cirata -v to see TLS fallback warnings from the CLI

See Also