Routing Modes — Subpath vs Subdomain
Choose whether Scrydon's browser-facing apps share one hostname or use one hostname per app.
Scrydon's browser-facing apps support two deployment-time routing modes. The same application
images serve both modes; routing.mode selects the public URLs, base paths, Ingress objects,
cookies, and CORS configuration.
Subpath (subpath, default) | Subdomain (subdomain) | |
|---|---|---|
| Example URLs | app.example.com/cortexapp.example.com/analytics | cortex.example.comanalytics.example.com |
| DNS | One A record | One wildcard or one A record per app |
| TLS | One host certificate | One wildcard or one certificate per app |
| Ingress | One per distinct workload namespace (ingress-frontdoor when collapsed) | One Ingress per enabled browser/realtime app |
| Cookies | Host-scoped | Shared registrable domain |
| CORS | One public origin | One public origin per app |
| Best fit | Customer and on-premises installs | SaaS or independent app domains |
Use the default subpath mode unless you need separate DNS, TLS, or SSO policy per app.
How base paths work
The chart resolves each application's prefix and injects BASE_PATH. The app mounts routes and
assets below that prefix, injects the value into client code, and configures TanStack Router
with the same base path. API services follow the same rule.
Ingress-only strip-prefix rewriting is insufficient because server-rendered pages and hashed assets need an application-aware public base path. No rebuild is required when changing modes.
Subpath quick start
routing:
mode: subpath
host: app.example.com
# paths:
# cortex: /cortex
# agentic: /agentic
# analytics: /analytics
# platform: /platform
# apiAuth: /api/auth
# apiOntology: /api/ontology
# apiTable: /api/table
# agenticRealtime: /agentic/realtimeCreate one A record for app.example.com. With TLS enabled, the chart references
tls-frontdoor and renders one ingress-frontdoor containing the enabled app and API paths.
Subdomain quick start
routing:
mode: subdomain
auth:
publicUrl: https://auth.example.com
ingress: { hostname: auth.example.com }
corsOrigins:
- https://app.example.com
- https://cortex.example.com
- https://agentic.example.com
- https://ws-agentic.example.com
- https://analytics.example.com
platform:
publicUrl: https://app.example.com
ingress: { hostname: app.example.com }
cortex:
publicUrl: https://cortex.example.com
ingress: { hostname: cortex.example.com }
agentic:
publicUrl: https://agentic.example.com
publicSocketUrl: https://ws-agentic.example.com
app:
ingress: { hostname: agentic.example.com }
realtime:
ingress:
hostname: ws-agentic.example.com
annotations:
traefik.ingress.kubernetes.io/service.sticky.cookie: "true"
traefik.ingress.kubernetes.io/service.sticky.cookie.name: scrydon_realtime_affinity
traefik.ingress.kubernetes.io/service.sticky.cookie.secure: "true"
traefik.ingress.kubernetes.io/service.sticky.cookie.httponly: "true"
analytics:
publicUrl: https://analytics.example.com
ingress: { hostname: analytics.example.com }Create an A record per host, or use a wildcard. Each enabled app Ingress references its corresponding TLS secret. Cross-subdomain sessions require every public URL to share the same registrable domain.
Marimo notebooks
Marimo does not have a public host, path, Ingress, or TLS secret. The notebook document, Connect request, cell Run request, HTTP proxying, and WebSocket proxying all stay on the authenticated Analytics origin:
- subpath:
https://app.example.com/analytics/notebooks/... - subdomain:
https://analytics.example.com/notebooks/...
The stateless document renderer and isolated runtimes are private cluster services. Do not
create a marimo.example.com DNS record or expose runtime ports directly.
DNS and TLS
For issuer choices, private domains, and corporate certificates, see TLS Certificates.
Subpath requires only:
app.example.com A <ingress load-balancer IP>Subdomain commonly requires:
app.example.com A <ingress load-balancer IP>
cortex.example.com A <ingress load-balancer IP>
agentic.example.com A <ingress load-balancer IP>
ws-agentic.example.com A <ingress load-balancer IP>
analytics.example.com A <ingress load-balancer IP>
auth.example.com A <ingress load-balancer IP>Switching a live deployment
- Provision the target DNS records and certificates.
- Change
routing.modeand the matching host/public URL values. - Upgrade the same chart version with your values file.
- Verify Ingresses, certificates, app health, sign-in, and WebSocket routes.
- Cut over DNS. Users may need to sign in again because cookie scope changed.
helm upgrade scrydon oci://scrydonops.azurecr.io/scrydon/charts/scrydon \
--version "${CURRENT_VERSION}" \
--namespace scrydon-platform \
-f values.customer.yaml \
--wait
# Subpath: one shared-host Ingress per distinct workload namespace
kubectl get ingress -A
# Subdomain: one Ingress per enabled public app
kubectl get ingress -AChanging the mode back and upgrading performs the routing rollback. It does not affect notebook runtime isolation because Marimo remains behind Analytics in both modes.
Multiple namespaces
An Ingress can reference only Services in its own namespace. In subpath mode the chart therefore
renders one front-door Ingress per distinct application namespace. Every resource uses the same
routing.host, and each contains only the paths whose Services are local to that namespace. A
collapsed install still renders only ingress-frontdoor in scrydon-platform.
When TLS is enabled, tls-frontdoor (or ingress.tls.existingSecret) must exist in every
namespace that receives a front-door Ingress. cert-manager creates those namespace-local copies
when enabled; static-certificate operators must copy the Secret themselves.