-
Notifications
You must be signed in to change notification settings - Fork 0
Custom Domains and TLS
Map a public domain to a realm and terminate TLS at the edge. The one thing to get right first: a custom domain adds a public name and certificate, terminating at the edge — it does not change identity inside the mesh, and a valid certificate is transport, not authorization. The domain still routes through the spine and traverses G0–G4 like any other host.
Hostnames are illustrative; Realm field names are the derived config model; the Istio and cert-manager resources are real APIs.
Two TLS layers exist — do not conflate them:
- External TLS — client ↔ edge, a public-CA certificate (ACME / Let's Encrypt) for the custom domain. This is what a browser validates.
-
Internal mTLS — workload ↔ workload inside the mesh, SPIFFE SVIDs, trust domain
norngate.com. The custom domain never touches this.
And one rule: the custom domain must route through the spine to a realm. A public host that reaches a runtime without traversing the gates is a hole, not a feature — total mediation applies to every host, named or not.
CNAME the custom domain to the edge endpoint. The edge (Alfheim) terminates TLS and serves cacheable reads close to clients; dynamic requests continue through the spine.
app.acme.com. CNAME edge.norngate.com.
Why the edge: terminating and caching at Alfheim keeps latency low for clients far from the core, while every request that changes state still flows to a realm and its gates.
Issue the certificate with cert-manager (ACME), or bring your own as a Secret.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata: { name: acme-app-cert, namespace: asgard }
spec:
secretName: acme-app-tls
dnsNames: ["app.acme.com"]
issuerRef: { name: letsencrypt, kind: ClusterIssuer }
Terminate and redirect at the Istio Gateway — TLS 1.3 minimum, HTTP forced to HTTPS.
apiVersion: networking.istio.io/v1
kind: Gateway
metadata: { name: acme-gateway, namespace: asgard }
spec:
selector: { istio: ingressgateway }
servers:
- port: { number: 443, name: https, protocol: HTTPS }
hosts: ["app.acme.com"]
tls: { mode: SIMPLE, credentialName: acme-app-tls, minProtocolVersion: TLSV1_3 }
- port: { number: 80, name: http, protocol: HTTP }
hosts: ["app.acme.com"]
tls: { httpsRedirect: true }
Route the host to the realm's runtime.
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata: { name: acme-route, namespace: midgard }
spec:
hosts: ["app.acme.com"]
gateways: ["asgard/acme-gateway"]
http:
- route:
- destination: { host: midgard-runtime.midgard.svc.cluster.local }
Why cert-manager: it automates issuance and renewal, so short-lived certs rotate without a human — the failure mode of custom domains is almost always an expired cert nobody renewed. Why TLS 1.3 + redirect: the minimum version defeats downgrade attacks and the redirect closes the plaintext door on port 80.
The custom host traverses G0–G4 exactly like an internal one. External clients authenticate at G0 with an OIDC token or API key (admitted through Vanaheim) — the public certificate proves the server's identity to the client and says nothing about the client's authorization.
Why this is the whole point: the common mistake is treating "it is HTTPS behind our domain" as security. TLS gets you an encrypted channel, not a permitted action — authorization is still G1, approval still G2. The gates are the control; the certificate is just the envelope.
The domain is not done when the padlock appears — it is done when the padlock appears and the request is gated.
-
Certificate — valid chain, matches the host, not expired; confirm cert-manager auto-renews ahead of expiry.
openssl s_client -connect app.acme.com:443 -servername app.acme.com </dev/null 2>/dev/null | openssl x509 -noout -dates - Posture — TLS 1.3 negotiated; HTTP→HTTPS redirect works.
-
Gated (the critical check) — a request to the custom domain must produce gate records:
If a request to
norngate-cli audit query --realm midgard --from now-5mapp.acme.comproduced no gate records, it bypassed the spine — stop and fix the routing before going live.
Why the gated check is non-negotiable: a green padlock on an ungated path is a false sense of security — encrypted, and completely unmediated. Encryption without mediation is the exact failure this platform exists to prevent.
- Setting Up Subdomains (Realms) — the realm the domain routes to.
- The Spine — the gateway, mesh, and mTLS.
- The Nine Worlds — Alfheim (edge) and Vanaheim (federated).
- The Five Gates — what the host still has to pass.
Section glyphs are Elder Futhark runes (Unicode Runic block, U+16A0–U+16FF) — semantic, not emoji. Full set on Home:
| Rune | Name | Gloss | Marks |
|---|---|---|---|
| ᚾ | Nauðiz | need, constraint | the platform mark |
| ᚨ | Ansuz | the word, instruction | before you start |
| ᛚ | Laguz | water, the flow | point DNS at the edge |
| ᛖ | Ehwaz | the horse, conveyance | terminate TLS at the gateway |
| ᚦ | Thurisaz | thorn, the gates | keep it gated |
| ᛒ | Berkanan | growth, the change verified | verify |
| ᚱ | Raidō | the ride, the road | next steps |
References. RFC 8555 (ACME) via cert-manager; RFC 8446 (TLS 1.3); RFC 6797 (HSTS); Istio Gateway / VirtualService (per The Spine); SPIFFE (internal mTLS, distinct from edge TLS). Naming doctrine: Prose Edda / Poetic Edda, per Norse Cosmology & Platform Design.
Map a public domain to a realm and terminate TLS at the edge. The one thing to get right first: a custom domain adds a public name and certificate, terminating at the edge — it does not change identity inside the mesh, and a valid certificate is transport, not authorization. The domain still routes through the spine and traverses G0–G4 like any other host.
Hostnames are illustrative; Realm field names are the derived config model; the Istio and cert-manager resources are real APIs.
Two TLS layers exist — do not conflate them:
- External TLS — client ↔ edge, a public-CA certificate (ACME / Let's Encrypt) for the custom domain. This is what a browser validates.
-
Internal mTLS — workload ↔ workload inside the mesh, SPIFFE SVIDs, trust domain
norngate.com. The custom domain never touches this.
And one rule: the custom domain must route through the spine to a realm. A public host that reaches a runtime without traversing the gates is a hole, not a feature — total mediation applies to every host, named or not.
CNAME the custom domain to the edge endpoint. The edge (Alfheim) terminates TLS and serves cacheable reads close to clients; dynamic requests continue through the spine.
app.acme.com. CNAME edge.norngate.com.
Why the edge: terminating and caching at Alfheim keeps latency low for clients far from the core, while every request that changes state still flows to a realm and its gates.
Issue the certificate with cert-manager (ACME), or bring your own as a Secret.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata: { name: acme-app-cert, namespace: asgard }
spec:
secretName: acme-app-tls
dnsNames: ["app.acme.com"]
issuerRef: { name: letsencrypt, kind: ClusterIssuer }Terminate and redirect at the Istio Gateway — TLS 1.3 minimum, HTTP forced to HTTPS.
apiVersion: networking.istio.io/v1
kind: Gateway
metadata: { name: acme-gateway, namespace: asgard }
spec:
selector: { istio: ingressgateway }
servers:
- port: { number: 443, name: https, protocol: HTTPS }
hosts: ["app.acme.com"]
tls: { mode: SIMPLE, credentialName: acme-app-tls, minProtocolVersion: TLSV1_3 }
- port: { number: 80, name: http, protocol: HTTP }
hosts: ["app.acme.com"]
tls: { httpsRedirect: true }Route the host to the realm's runtime.
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata: { name: acme-route, namespace: midgard }
spec:
hosts: ["app.acme.com"]
gateways: ["asgard/acme-gateway"]
http:
- route:
- destination: { host: midgard-runtime.midgard.svc.cluster.local }Why cert-manager: it automates issuance and renewal, so short-lived certs rotate without a human — the failure mode of custom domains is almost always an expired cert nobody renewed. Why TLS 1.3 + redirect: the minimum version defeats downgrade attacks and the redirect closes the plaintext door on port 80.
The custom host traverses G0–G4 exactly like an internal one. External clients authenticate at G0 with an OIDC token or API key (admitted through Vanaheim) — the public certificate proves the server's identity to the client and says nothing about the client's authorization.
Why this is the whole point: the common mistake is treating "it is HTTPS behind our domain" as security. TLS gets you an encrypted channel, not a permitted action — authorization is still G1, approval still G2. The gates are the control; the certificate is just the envelope.
The domain is not done when the padlock appears — it is done when the padlock appears and the request is gated.
-
Certificate — valid chain, matches the host, not expired; confirm cert-manager auto-renews ahead of expiry.
openssl s_client -connect app.acme.com:443 -servername app.acme.com </dev/null 2>/dev/null | openssl x509 -noout -dates
- Posture — TLS 1.3 negotiated; HTTP→HTTPS redirect works.
-
Gated (the critical check) — a request to the custom domain must produce gate records:
If a request to
norngate-cli audit query --realm midgard --from now-5m
app.acme.comproduced no gate records, it bypassed the spine — stop and fix the routing before going live.
Why the gated check is non-negotiable: a green padlock on an ungated path is a false sense of security — encrypted, and completely unmediated. Encryption without mediation is the exact failure this platform exists to prevent.
- [Setting Up Subdomains (Realms)](Setting-Up-Subdomains-Realms) — the realm the domain routes to.
- [The Spine](The-Spine) — the gateway, mesh, and mTLS.
- [The Nine Worlds](The-Nine-Worlds) — Alfheim (edge) and Vanaheim (federated).
- [The Five Gates](The-Five-Gates) — what the host still has to pass.
Section glyphs are Elder Futhark runes (Unicode Runic block, U+16A0–U+16FF) — semantic, not emoji. Full set on [Home](Home#iconography):
| Rune | Name | Gloss | Marks |
|---|---|---|---|
| ᚾ | Nauðiz | need, constraint | the platform mark |
| ᚨ | Ansuz | the word, instruction | before you start |
| ᛚ | Laguz | water, the flow | point DNS at the edge |
| ᛖ | Ehwaz | the horse, conveyance | terminate TLS at the gateway |
| ᚦ | Thurisaz | thorn, the gates | keep it gated |
| ᛒ | Berkanan | growth, the change verified | verify |
| ᚱ | Raidō | the ride, the road | next steps |
References. [RFC 8555](https://www.rfc-editor.org/rfc/rfc8555) (ACME) via [cert-manager](https://cert-manager.io/); [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446) (TLS 1.3); [RFC 6797](https://www.rfc-editor.org/rfc/rfc6797) (HSTS); Istio Gateway / VirtualService (per [The Spine](The-Spine)); [SPIFFE](https://spiffe.io/) (internal mTLS, distinct from edge TLS). Naming doctrine: Prose Edda / Poetic Edda, per [Norse Cosmology & Platform Design](Norse-Cosmology-and-Platform-Design).
Welcome
Getting Started
Core Concepts
Guides
- Guides
- Configuring Gates
- Setting Up Subdomains (Realms)
- Integrating AI Agents
- Monitoring & Observability
- Disaster Recovery (Ragnarök Drill)
- Custom Domains & TLS
Reference
Silence means no.