loom.hypertheory-labs.dev serves a documentation site that exists only here, so the Gateway no longer matches production listener for listener. A route pinned to loom will not apply against production and it is the only one of which that is true. Recorded as a divergence with an explicit end state, like the last plain Ingress, rather than silently weakening the invariant the page opens with. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
101 lines
4.1 KiB
Markdown
101 lines
4.1 KiB
Markdown
# Put something on the internet, the way production would
|
|
|
|
**This cluster is staging for the production cluster**, and that is the reason it
|
|
exists rather than a use it happens to have.
|
|
|
|
> **Production's route manifests apply here unmodified.** *The only difference
|
|
> between the two Gateway definitions is the TLD — `.dev` here, `.com` there.
|
|
> Secret names are identical.*
|
|
|
|
**So the point of this page is what to leave out**, not what to add.
|
|
|
|
---
|
|
|
|
## Carry no hostname of your own
|
|
|
|
**Routes pin to a listener by `sectionName` and inherit its hostname.** *That is
|
|
what makes them portable — a route that carries its own `hostnames:` stops
|
|
inheriting and stops working in the other environment.*
|
|
|
|
**Four listeners match production one for one:** `web` (plain HTTP), and
|
|
`websecure`, `theoria` and `notes` over HTTPS. **Pin to the one whose hostname you
|
|
want.**
|
|
|
|
**A fifth, `loom`, has no production counterpart.** *It serves a documentation
|
|
site that exists only here.* **So the one-for-one claim now holds of four
|
|
listeners and not of the Gateway** — *a route pinned to `loom` will not apply
|
|
against production, and that is the only listener of which that is true.*
|
|
|
|
> **A divergence with an explicit end state**, like the plain `Ingress` below:
|
|
> *either production grows a `loom` listener, or this stays staging-only and
|
|
> production keeps four.* **Written down so it cannot become permanent by nobody
|
|
> remembering it was a choice.**
|
|
|
|
**One route is the exception and keeps its hostname:** the HTTP-to-HTTPS
|
|
redirect, because the `web` listener declares no hostname at all — *dropping it
|
|
there would widen the redirect from one host to every host.*
|
|
|
|
## The Gateway is not in the controller's namespace, on purpose
|
|
|
|
**The Gateway lives in `traefik`; the controller runs in `traefik-system`.**
|
|
|
|
*Only the **Gateway's** namespace affects how `parentRefs` resolve, and
|
|
production names `traefik`* — **so production's refs resolve here verbatim.**
|
|
Moving the controller would have meant an uninstall against a live service for
|
|
something no application can observe.
|
|
|
|
## Plain HTTP does not answer for everything
|
|
|
|
**Only one hostname redirects from HTTP.** *The others have no route on the `web`
|
|
listener and return 404 on plain HTTP.* **That mirrors production exactly**, and
|
|
it is a consequence of the exception above rather than an oversight.
|
|
|
|
## A typo attaches to nothing, silently
|
|
|
|
**A route with a wrong `sectionName` binds to no listener and reports nothing.**
|
|
|
|
```sh
|
|
kubectl get gateway traefik-gateway -n traefik \
|
|
-o jsonpath='{range .status.listeners[*]}{.name}: attached={.attachedRoutes}{"\n"}{end}'
|
|
```
|
|
|
|
> **`attachedRoutes` is the number that actually bound.** *Check it, not whether
|
|
> the route exists.*
|
|
|
|
## TLS is issued here and only terminated here
|
|
|
|
**cert-manager owns every certificate**, via DNS-01 against Cloudflare. **Do not
|
|
configure the proxy's own ACME resolver.**
|
|
|
|
*A certificate issues without the hostname resolving publicly, because the
|
|
challenge is DNS-based. **Iterate against the staging issuer** — production rate
|
|
limits are easy to hit during a rebuild.*
|
|
|
|
*A failed issuance backs off starting at **one hour**, and deleting the
|
|
`CertificateRequest` does not reset it — it looks hung and is not. **Delete and
|
|
re-apply the `Certificate`.***
|
|
|
|
## The older path still works
|
|
|
|
**One service is on a plain `Ingress`** rather than the Gateway, and stays that
|
|
way until it is migrated. *Production uses no `Ingress` at all; when that last one
|
|
moves, the environments match exactly.* **The divergence has an explicit end
|
|
state, so it cannot quietly become permanent.**
|
|
|
|
---
|
|
|
|
## Checking this is still true
|
|
|
|
**Verified 2026-09-08.**
|
|
|
|
```sh
|
|
kubectl get gatewayclass # ACCEPTED=True
|
|
kubectl get gateway -n traefik # PROGRAMMED=True, with an address
|
|
curl -sS -o /dev/null -w "%{http_code}\n" https://loom.hypertheory-labs.dev/
|
|
curl -sS -o /dev/null -w "%{http_code}\n" https://class.hypertheory-labs.dev/
|
|
|
|
# A hostname with no listener MUST 404. A 200 means a route was widened.
|
|
curl -skS -o /dev/null -w "%{http_code}\n" \
|
|
--resolve bogus.hypertheory-labs.dev:443:<any node IP> https://bogus.hypertheory-labs.dev/
|
|
```
|