From bbc8f141e342787890a7d570ed205bffe78408fc Mon Sep 17 00:00:00 2001 From: Demisu <19689915+Demisu@users.noreply.github.com> Date: Mon, 26 Oct 2026 22:09:45 +0100 Subject: [PATCH] feat(helm): add optional Gateway API HTTPRoute support Add disabled-by-default HTTPRoute support with configurable parent references, hostnames, paths, annotations and optional timeouts. Route to the existing TREK Service and keep Ingress support unchanged, including simultaneous use. Document configuration and extend the existing Helm Chart & Compose job with HTTPRoute render variants and strict validation using a pinned CRDs catalog. Approved on Discord: https://discord.com/channels/1488298068427411591/1553537637455761500 Validation: strict Helm lint, 37 manifests validated without errors or skipped resources, and both Compose configuration checks passed locally. Live Gateway WebSocket and streamed assistant response checks were not performed because no Kubernetes test cluster was configured. --- .github/workflows/test.yml | 17 +++++++++++- charts/README.md | 30 +++++++++++++++++++-- charts/trek/templates/NOTES.txt | 9 +++++++ charts/trek/templates/httproute.yaml | 39 ++++++++++++++++++++++++++++ charts/trek/values.yaml | 19 +++++++++++++- wiki/Install-Helm.md | 25 ++++++++++++++++++ 6 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 charts/trek/templates/httproute.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 501e9c488..f9e32a38a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1270,9 +1270,24 @@ jobs: --set 'extraVolumeMounts[0].readOnly=true' \ --set probes.liveness=null \ > "$out/variants.yaml" + helm template trek charts/trek \ + --set httpRoute.enabled=true \ + --set 'httpRoute.parentRefs[0].name=gateway' \ + > "$out/http-route.yaml" + helm template trek charts/trek \ + --set ingress.enabled=true \ + --set fullnameOverride=custom-trek \ + --set service.port=8080 \ + --set-json 'httpRoute={"enabled":true,"parentRefs":[{"name":"gateway","namespace":"gateway-system","sectionName":"https"}],"hostnames":["trek.example.com"],"paths":["/","/api"],"annotations":{"example.com/note":"custom route"},"timeouts":{"request":"0s","backendRequest":"0s"}}' \ + > "$out/http-route-variants.yaml" - name: Check the manifests against the Kubernetes schemas - run: kubeconform -strict -summary "$RUNNER_TEMP"/rendered/*.yaml + # The pinned Gateway API schema adds HTTPRoute validation without skipping unknown resources. + run: >- + kubeconform -strict -summary + -schema-location default + -schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/041baf4c1d3740ca21461cab765e7432ecd073ef/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' + "$RUNNER_TEMP"/rendered/*.yaml - name: Check every compose file run: | diff --git a/charts/README.md b/charts/README.md index 7f9c11540..a31fa2cdf 100644 --- a/charts/README.md +++ b/charts/README.md @@ -8,6 +8,7 @@ This is a minimal Helm chart for deploying the TREK app. - Optional persistent storage for `/app/data` and `/app/uploads`, plus extra volumes and mounts - Configurable environment variables and secrets - Optional generic Ingress support +- Optional Gateway API HTTPRoute support - Configurable health checks (liveness/readiness probes) on `/api/health` ## Helm Repository @@ -27,13 +28,38 @@ helm install trek trek/trek Or install directly from the local chart: ```sh -helm install trek ./chart \ +helm install trek ./charts/trek \ --set ingress.enabled=true \ --set ingress.hosts[0].host=yourdomain.com ``` See `values.yaml` for more options. +### HTTPRoute + +HTTPRoute support is off by default. Enable it to attach TREK to an existing Gateway: + +```yaml +httpRoute: + enabled: true + annotations: {} + parentRefs: + - name: gateway + namespace: gateway-system + sectionName: https + hostnames: + - trek.example.com + paths: + - / + timeouts: + request: "0s" + backendRequest: "0s" +``` + +The cluster must have the Gateway API `HTTPRoute` v1 CRD and a Gateway API controller. The chart creates only the HTTPRoute, targeting TREK's existing Service and `service.port`; the Gateway, GatewayClass, listener, TLS certificate, and `allowedRoutes` configuration remain the operator's responsibility. When enabled, `parentRefs` and `paths` must be non-empty. Each path is a string prefix. Ingress and HTTPRoute can both be enabled. + +Timeout fields became Standard Channel in Gateway API v1.2, but controller support is implementation-specific. Omitting them leaves timeout behavior to the controller. `0s` disables the corresponding request timeout; controller idle and buffering settings can still interrupt `/ws` connections or streamed assistant responses. Set `env.ALLOWED_ORIGINS` and `env.APP_URL` to the public URL; when the Gateway terminates TLS, configure `env.FORCE_HTTPS: "true"` and `env.TRUST_PROXY: "1"`. + ## Files - `Chart.yaml` — chart metadata - `values.yaml` — configuration values @@ -47,7 +73,7 @@ See `values.yaml` for more options. - `JWT_SECRET` is managed entirely by the server — auto-generated into the data PVC on first start and rotatable via the admin panel (Settings → Danger Zone). No Helm configuration needed. - `ENCRYPTION_KEY` encrypts stored secrets (API keys, MFA, SMTP, OIDC) at rest. Recommended: set via `secretEnv.ENCRYPTION_KEY` or `existingSecret`. If left empty, the server falls back automatically: existing installs use `data/.jwt_secret` (no action needed on upgrade); fresh installs auto-generate a key persisted to the data PVC. - Liveness and readiness probes are configurable under `probes`. Overrides are merged over the defaults, so `--set probes.liveness.initialDelaySeconds=60` keeps the `httpGet` handler. When switching to `exec` or `tcpSocket`, clear `httpGet` in the same override. Set `probes.liveness: null` to remove a probe. -- If using ingress, you must manually keep `env.ALLOWED_ORIGINS` and `ingress.hosts` in sync to ensure CORS works correctly. The chart does not sync these automatically. +- If exposing TREK through an Ingress or HTTPRoute, set `env.ALLOWED_ORIGINS` and `env.APP_URL` to the public URL. The chart does not sync these automatically. - Set `env.ALLOW_INTERNAL_NETWORK: "true"` if Immich or other integrated services are hosted on a private/RFC-1918 address (e.g. a pod on the same cluster or a NAS on your LAN). Loopback (`127.x`) and link-local/metadata addresses (`169.254.x`) remain blocked regardless. - Set `env.ALLOW_LINK_LOCAL_IPS` to a single link-local address (e.g. `"169.254.1.2"`) only when a service TREK needs sits behind one, such as a host gateway. Cloud metadata addresses (`169.254.169.x`, `169.254.170.x`) cannot be listed. - `FORCE_HTTPS` is optional. Set `env.FORCE_HTTPS: "true"` only when ingress (or another proxy) terminates TLS. It enables HTTPS redirects, HSTS, CSP `upgrade-insecure-requests`, and forces the session cookie `secure` flag. Requires `TRUST_PROXY` to be set. diff --git a/charts/trek/templates/NOTES.txt b/charts/trek/templates/NOTES.txt index 7845bc00d..6562dd60d 100644 --- a/charts/trek/templates/NOTES.txt +++ b/charts/trek/templates/NOTES.txt @@ -31,3 +31,12 @@ The other values for that volume (size, storageClassName, annotations) are then ignored. - With `persistence.enabled=false` the volumes use an emptyDir — storage is ephemeral and is lost when the pod restarts. Use only for testing. + +{{- if .Values.httpRoute.enabled }} +6. HTTPRoute: + - The chart creates an HTTPRoute v1 to the existing TREK Service. The Gateway, GatewayClass, + listeners, TLS certificates and allowedRoutes are managed by your cluster operator. + - Ingress and HTTPRoute may be enabled together. + - Timeout fields are Standard Channel in Gateway API v1.2, but controller support varies. Omit + them to use controller defaults; `0s` disables the corresponding timeout where supported. +{{- end }} diff --git a/charts/trek/templates/httproute.yaml b/charts/trek/templates/httproute.yaml new file mode 100644 index 000000000..30c49cae0 --- /dev/null +++ b/charts/trek/templates/httproute.yaml @@ -0,0 +1,39 @@ +{{- if .Values.httpRoute.enabled -}} +{{- if not .Values.httpRoute.parentRefs }} +{{- fail "httpRoute.parentRefs must include at least one parent" }} +{{- end }} +{{- if not .Values.httpRoute.paths }} +{{- fail "httpRoute.paths must include at least one path" }} +{{- end }} +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: {{ include "trek.fullname" . }} + labels: + app: {{ include "trek.name" . }} + {{- with .Values.httpRoute.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + parentRefs: + {{- toYaml .Values.httpRoute.parentRefs | nindent 4 }} + {{- with .Values.httpRoute.hostnames }} + hostnames: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + {{- range $path := .Values.httpRoute.paths }} + - matches: + - path: + type: PathPrefix + value: {{ $path | quote }} + backendRefs: + - name: {{ include "trek.fullname" $ }} + port: {{ $.Values.service.port }} + {{- with $.Values.httpRoute.timeouts }} + timeouts: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/trek/values.yaml b/charts/trek/values.yaml index 35e9f751f..deaeb8e13 100644 --- a/charts/trek/values.yaml +++ b/charts/trek/values.yaml @@ -36,7 +36,7 @@ env: # Browser/OS language is auto-detected first; this is the fallback when no match is found. # Supported: ar, az, br, ca, cs, de, en, es, et, fr, gr, hu, id, it, ja, ko, nl, pl, ru, sk, sv, th, tr, uk, vi, zh, zh-TW # ALLOWED_ORIGINS: "" - # NOTE: If using ingress, ensure env.ALLOWED_ORIGINS matches the domains in ingress.hosts for proper CORS configuration. + # NOTE: If using ingress or HTTPRoute, ensure env.ALLOWED_ORIGINS matches the public domains for proper CORS configuration. # APP_URL: "https://trek.example.com" # Public base URL of this instance. Required when OIDC is enabled — must match the redirect URI registered with your IdP. # Also used as the base URL for links in email notifications and other external links. @@ -294,3 +294,20 @@ ingress: # - secretName: chart-example-tls # hosts: # - chart-example.local + +httpRoute: + enabled: false + annotations: {} + parentRefs: [] + # - name: gateway + # namespace: gateway-system + # sectionName: https + hostnames: [] + # - trek.example.com + paths: + - / + # Timeout fields are Standard Channel in Gateway API v1.2; controller support varies. + # Leave empty for controller defaults. Use "0s" to disable the corresponding timeout. + timeouts: {} + # request: "0s" + # backendRequest: "0s" diff --git a/wiki/Install-Helm.md b/wiki/Install-Helm.md index b1a1cab76..6dfd290a2 100644 --- a/wiki/Install-Helm.md +++ b/wiki/Install-Helm.md @@ -271,6 +271,31 @@ ingress: > **Note:** When using ingress with TLS termination, set `env.FORCE_HTTPS: "true"` and `env.TRUST_PROXY: "1"` to enable HTTPS redirects, HSTS, and secure cookies. +### HTTPRoute + +HTTPRoute support is off by default. To attach TREK to an existing Gateway, enable it in your values file: + +```yaml +httpRoute: + enabled: true + annotations: {} + parentRefs: + - name: gateway + namespace: gateway-system + sectionName: https + hostnames: + - trek.example.com + paths: + - / + timeouts: + request: "0s" + backendRequest: "0s" +``` + +The cluster needs the Gateway API `HTTPRoute` v1 CRD and a compatible controller. The chart creates only an HTTPRoute to TREK's existing Service on `service.port`; your operator must provide the Gateway and GatewayClass, configure its listener and TLS certificate, and allow routes from the chart's namespace. When enabled, `parentRefs` and `paths` must be non-empty. Paths are string prefixes. Ingress and HTTPRoute may be enabled together. + +Timeout fields are Standard Channel in Gateway API v1.2, though controller support varies. If omitted, the controller's timeout defaults apply. `0s` disables that request timeout; controller idle and buffering limits can still affect `/ws` and streamed assistant responses. Set `env.ALLOWED_ORIGINS` and `env.APP_URL` to the public URL. If the Gateway terminates TLS, also set `env.FORCE_HTTPS: "true"` and `env.TRUST_PROXY: "1"`. + ## Upgrade The Deployment uses the `Recreate` update strategy (`updateStrategy: Recreate`): the old pod stops before the new one starts. TREK is a single-writer SQLite application, and during a rolling update the new pod would migrate the database while the old one still writes to it. A ReadWriteMany volume does not make that safe, so leave the strategy as it is and keep one replica.