TLS Certificates
Choose and install certificates for Scrydon's public edge, private domains, and Traefik-backed Kubernetes routing.
Scrydon exposes browser and API traffic through Kubernetes Gateway API objects — one Gateway and one HTTPRoute per enabled app. The chart defaults to the traefik GatewayClass and stamps cert-manager.io/cluster-issuer: letsencrypt-prod on the Gateway, but certificate ownership depends on where TLS terminates.
Choose your path
| Your setup | Certificate path |
|---|---|
| Public hostname reaches Traefik directly | Use cert-manager with Let's Encrypt. The TLS prerequisite shows the default ClusterIssuer. |
| Publicly delegated domain, but private ingress | Use Let's Encrypt DNS-01 with a DNS provider solver. |
| Private-only hostname | Use an internal ACME CA or a static corporate certificate. |
| Upstream load balancer terminates TLS | Install the public certificate on the load balancer, then configure TLS Offloading. |
| Upstream load balancer re-encrypts, or clients reach Traefik directly over HTTPS | Give Traefik a certificate for the public hostname. |
Certificate responsibilities
| Leg | What needs a certificate | Default Scrydon behavior |
|---|---|---|
| Client to Traefik | The certificate that clients see for routing.host or the per-app subdomains. This can live on Traefik, or on an upstream load balancer if it terminates TLS before Traefik. | In subpath mode the Gateway's https listener references tls-frontdoor. In subdomain mode the Gateway gets one HTTPS listener per enabled app, referencing tls-auth, tls-platform, tls-cortex, tls-agentic, tls-agentic-realtime, and tls-analytics as needed. Every one of those Secrets is read from the Gateway's own namespace (scrydon-platform), even in a split-namespace install. Marimo stays on the authenticated Analytics origin and needs no separate certificate. |
| Traefik to workloads | No public certificate by default. Traefik routes to the app Services over the cluster network. | The chart's HTTPRoute backendRefs target each app Service's HTTP port. Service-to-service traffic between Scrydon workloads is protected separately by Dapr mTLS with SPIFFE identity. |
Keep gateway.tls.enabled: true whenever users reach Scrydon over https://, even if an upstream load balancer terminates TLS and forwards plain HTTP to Traefik. This value controls public URL generation, CORS, and secure cookies; it is not only a Traefik certificate toggle.
If your upstream load balancer terminates TLS and forwards HTTP to Traefik, the public certificate belongs on that load balancer. Traefik still needs trusted forwarded headers; follow TLS Offloading.
If your upstream load balancer re-encrypts to Traefik, or users connect directly to Traefik over HTTPS, Traefik needs a certificate for the public hostname.
Public DNS and Let's Encrypt
Let's Encrypt can issue only for names under publicly delegated DNS. HTTP-01 also requires the hostname to resolve publicly to an ingress that can serve /.well-known/acme-challenge/... on port 80.
DNS-01 does not require the A record to be public, but it still requires a publicly delegated domain where Let's Encrypt can query the authoritative DNS server for _acme-challenge TXT records. For example:
| Domain shape | Let's Encrypt fit |
|---|---|
app.example.com, where example.com is publicly registered and you can create public DNS TXT records | Works. Use HTTP-01 if ingress is public, or DNS-01 if the A record is private. |
app.internal, something.local, or any private-only zone with no public authoritative DNS | Does not work. Use an internal CA or static corporate certificate. |
Option 1: internal ACME with step-ca
For private-only domains where you still want automatic issuance and renewal, run an internal ACME CA such as Smallstep step-ca and point cert-manager at it.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: internal-acme-issuer
spec:
acme:
server: https://step-ca.cert-manager.svc.cluster.local/acme/acme/directory
email: admin@yourcompany.local
privateKeySecretRef:
name: internal-acme-account-key
solvers:
- http01:
gatewayHTTPRoute:
parentRefs:
- kind: Gateway
name: gateway-frontdoor
namespace: scrydon-platformThen use that issuer in your Scrydon values:
gateway:
tls:
enabled: true
clusterIssuer: internal-acme-issuer
routing:
host: app.yourcompany.localcert-manager populates tls-frontdoor in the platform namespace, where the Gateway
lives. There is only ever one certificate request per hostname, in both routing
modes and whether or not you split namespaces — the chart carries a single
cert-manager.io/cluster-issuer annotation on the Gateway, and cert-manager groups
listeners by certificate Secret.
This requires cert-manager's Gateway API support (config.enableGatewayAPI=true);
see TLS prerequisites.
Option 2: bring your own static certificates
If your IT or security team issues certificates from a corporate PKI, create Kubernetes TLS Secrets with the names Scrydon's Gateway listeners reference. Set gateway.tls.certManager.enabled: false so cert-manager does not race your certificate with an ACME order, or point every listener at one Secret with gateway.tls.existingSecret.
For default subpath routing:
kubectl create secret tls tls-frontdoor \
--cert=path/to/corporate-cert.crt \
--key=path/to/corporate-key.key \
-n scrydon-platformKeep that Secret in scrydon-platform even when you override namespaces.agentic,
namespaces.analytics, or namespaces.cortex. The Gateway owns every listener, so
certificateRefs are resolved from the Gateway's namespace only — there is nothing
to copy into the other namespaces, on any implementation.
For subdomain routing, create the per-app secrets in the Gateway's namespace as well — the listeners all live on the one Gateway:
for name in tls-auth tls-platform tls-cortex tls-agentic tls-agentic-realtime tls-analytics; do
kubectl create secret tls "$name" \
--cert=path/to/corporate-wildcard-or-host-cert.crt \
--key=path/to/corporate-key.key \
-n scrydon-platform
doneIf you also want Traefik to have a fallback certificate for hostnames the Gateway's listeners do not cover, configure its default TLSStore:
apiVersion: traefik.io/v1alpha1
kind: TLSStore
metadata:
name: default
namespace: traefik
spec:
defaultCertificate:
secretName: traefik-public-tlsCreate that traefik-public-tls Secret in the traefik namespace if you use this fallback.
Client trust requirement
Private CA certificates are not trusted by browsers or API clients automatically. Every user device, server, and automation that connects to Scrydon's public endpoint must trust the issuing root or intermediate CA.
Distribute the corporate root CA through your normal device management channel, such as MDM, Active Directory Group Policy, Jamf, Intune, or your Linux base image. Without that root installed, clients will see certificate authority errors even when Traefik is configured correctly.
Verify
kubectl get gateway -n scrydon-platform
kubectl describe gateway gateway-frontdoor -n scrydon-platform # one condition block per listener
kubectl get certificate -A
kubectl -n scrydon-platform get secret tls-frontdoor
openssl s_client -connect app.example.com:443 -servername app.example.com </dev/nullCheck that the presented certificate's subject/SAN covers the public hostname and that its issuer chains to a CA trusted by your clients.