From 02ba91d91b3d18c9a4a4710a856b3f47df0a5b22 Mon Sep 17 00:00:00 2001 From: tada5hi Date: Wed, 19 Aug 2026 11:44:46 +0200 Subject: [PATCH 1/3] fix(authup): keep the global schema node open for umbrella parents helm copies a parent chart's ENTIRE global map into every subchart's values and then validates them against that subchart's own schema, both inside ToRenderValues and before a single template renders. The generated schema closed `global` with additionalProperties: false, so any umbrella chart that set a global this chart does not declare could not render at all: $ helm template u parent # parent values: global.myOrgTenant Error: values don't meet the specifications of the schema(s) in the following chart(s): authup: - global: Additional property myOrgTenant is not allowed That fires for global.imagePullPolicy, bitnami's global.storageClass and global.compatibility.*, and for global.security.allowInsecureImages, which DESIGN.md itself tells operators to set. No values file can work around it: nulling the key in the subchart section leaves it as an additional property, and nulling `global` outright trips the root-level required entry instead. `global` is the one node in a values tree a chart does not own. The `# @schema additionalProperties: true` opt-out also drops `global` from the root required list, so `authup: {global: null}` stays legal for consumers who want to block propagation. `type: object` is kept, so a scalar `global` still fails, and the three globals the chart consumes keep their types. Typo detection everywhere else is unaffected. ci/default-values.yaml carries a stray global key as the regression guard: `make template` and the kind matrix both render it. Closes #11 --- .agents/architecture.md | 9 +++++++++ .agents/testing.md | 5 +++++ charts/authup/Chart.yaml | 10 ++-------- charts/authup/ci/default-values.yaml | 5 +++++ charts/authup/values.schema.json | 3 +-- charts/authup/values.yaml | 4 ++++ 6 files changed, 26 insertions(+), 10 deletions(-) diff --git a/.agents/architecture.md b/.agents/architecture.md index 8104d98..9dc7c6f 100644 --- a/.agents/architecture.md +++ b/.agents/architecture.md @@ -91,6 +91,15 @@ editing templates or values. `authup.tplvalues.render`, so umbrella charts can inject template expressions (the PrivateAIM lesson: their untemplatable `existingSecret` forced a hardcoded-names table). +17. **`global` must stay open in the schema.** helm copies a parent chart's + ENTIRE `global` map into every subchart before validating that subchart's + schema, so `additionalProperties: false` there makes the chart + uninstallable as a dependency of any umbrella that sets a global this + chart does not declare. `values.yaml` carries the + `# @schema additionalProperties: true` opt-out and `ci/default-values.yaml` + a stray global key as the regression guard. The chart reads only + `imageRegistry` / `imagePullSecrets` / `defaultStorageClass` and ignores + the rest. ## Values conventions diff --git a/.agents/testing.md b/.agents/testing.md index 5533e4d..b788663 100644 --- a/.agents/testing.md +++ b/.agents/testing.md @@ -54,6 +54,11 @@ helm template t charts/authup --set server.theme.enabled=true --set 'server.them helm template t charts/authup --set 'server.trustedOrigins[0]=https://**.x' # globstar host ``` +Umbrella use is part of the contract: `global` must stay open. Render a throwaway +parent chart with authup in `charts/` and an unrelated global (`global.myOrgKey`) +whenever the schema generation changes; `ci/default-values.yaml` carries a stray +global key as the cheap in-repo version of that check. + A single `*` host wildcard (`https://*.example.com`) must still RENDER: authup supports it, only `**` is the allow-any-origin trap. diff --git a/charts/authup/Chart.yaml b/charts/authup/Chart.yaml index 20df41a..e4b6034 100644 --- a/charts/authup/Chart.yaml +++ b/charts/authup/Chart.yaml @@ -31,11 +31,5 @@ annotations: - name: Source url: https://github.com/authup/helm artifacthub.io/changes: | - - kind: changed - description: appVersion tracks authup 1.0.0-beta.62 - - kind: added - description: server.theme manifest values compose theme.json (title, logo, tokens) - - kind: added - description: server.features.accountConsole toggles the /account self-service console - - kind: added - description: Trusted origins carrying "**" now fail the render, as authup fails the boot + - kind: fixed + description: A parent chart's own global.* keys no longer fail schema validation diff --git a/charts/authup/ci/default-values.yaml b/charts/authup/ci/default-values.yaml index 8a84938..ec449d4 100644 --- a/charts/authup/ci/default-values.yaml +++ b/charts/authup/ci/default-values.yaml @@ -1,4 +1,9 @@ # Baseline: built-in PostgreSQL, both services. +# The stray global key is a regression guard: helm copies a parent chart's whole +# global map into every subchart before schema validation, so the schema must not +# reject globals this chart does not declare. +global: + umbrellaProbe: parent-injected postgresql: persistence: enabled: false diff --git a/charts/authup/values.schema.json b/charts/authup/values.schema.json index a6c28cc..332d332 100644 --- a/charts/authup/values.schema.json +++ b/charts/authup/values.schema.json @@ -1359,7 +1359,7 @@ "type": "string" }, "global": { - "additionalProperties": false, + "additionalProperties": true, "properties": { "defaultStorageClass": { "default": "", @@ -3890,7 +3890,6 @@ } }, "required": [ - "global", "nameOverride", "fullnameOverride", "namespaceOverride", diff --git a/charts/authup/values.yaml b/charts/authup/values.yaml index 677b5e4..17a10b3 100644 --- a/charts/authup/values.yaml +++ b/charts/authup/values.yaml @@ -2,6 +2,10 @@ ## @section Global parameters +# @schema +# type: object +# additionalProperties: true +# @schema global: # -- Global container image registry override (takes precedence over image.registry) imageRegistry: "" From cadf9c68c4911e9138d491c137ab69838445d669 Mon Sep 17 00:00:00 2001 From: tada5hi Date: Wed, 19 Aug 2026 11:45:20 +0200 Subject: [PATCH 2/3] feat(authup): express matches and filters on the HTTPRoute rule server.route / adminConsole.route rendered a single rule that was nothing but backendRefs. Per the Gateway API, a rule with no `matches` defaults to a PathPrefix "/" match, so that rule matches every request on every hostname the route attaches to. The route hostname is derived from the public URL's ORIGIN and authup.urlOrigin drops the path, which made the chart's own documented sub-path topology emit a silent catch-all: server.publicUrl: https://hub.example.com/auth server.route.enabled: true -> hostnames: [hub.example.com], rules: [{backendRefs: [...]}] With both services on one shared host the two routes tie on every Gateway API precedence rule and the alphabetical tie-break sends /auth to the admin console. No value could correct it: the route objects were closed (additionalProperties: false) with only enabled/hostnames/parentRefs/ annotations, and Gateway API has no annotation equivalent of the proxy-buffer-size workaround the chart documents for ingress-nginx (NGINX Gateway Fabric needs an ExtensionRef filter to a SnippetsFilter, a spec field). `matches` and `filters` are raw Gateway API passthrough, tpl-rendered like every other list value, so an umbrella can compose them from parent state. Both default to [] and the rendered output without them is byte-identical to before. This is the parity the Ingress renderer already had via extraPaths / extraHosts / extraTls / extraRules. validations.yaml now fails the render when a route is enabled, the public URL carries a non-root path and `matches` is empty, quoting the match and the URLRewrite filter to set. authup always serves at "/" and expects the proxy to strip the prefix, so the chart cannot derive the match on its own: a PathPrefix match without the rewrite would forward /auth/token to a server that only serves /token. The guard reads the URL after derivation, so an ingress-derived public URL is covered too. Also corrects two doc strings: ingress.extraRules is appended after the generated rules rather than overriding them, and route.hostnames notes that only the host survives the derivation. Closes #12 --- .agents/architecture.md | 7 ++++ .agents/testing.md | 8 ++++ charts/authup/Chart.yaml | 4 ++ charts/authup/README.md | 12 ++++-- charts/authup/templates/_ingress.tpl | 6 +++ charts/authup/templates/validations.yaml | 20 +++++++++ charts/authup/values.schema.json | 52 +++++++++++++++++++++--- charts/authup/values.yaml | 18 ++++++-- 8 files changed, 113 insertions(+), 14 deletions(-) diff --git a/.agents/architecture.md b/.agents/architecture.md index 9dc7c6f..92acbb2 100644 --- a/.agents/architecture.md +++ b/.agents/architecture.md @@ -100,6 +100,13 @@ editing templates or values. a stray global key as the regression guard. The chart reads only `imageRegistry` / `imagePullSecrets` / `defaultStorageClass` and ignores the rest. +18. **An HTTPRoute rule with no `matches` is a catch-all.** The Gateway API + defaults an empty `matches` to PathPrefix `/`, and route hostnames come + from the public URL's ORIGIN (the path is dropped), so a sub-path + deployment would silently take over the whole shared hostname. + `validations.yaml` fails that combination; `route.matches` / `route.filters` + are the raw passthroughs that express it (authup always serves at `/`, so + the prefix must be matched AND rewritten away). ## Values conventions diff --git a/.agents/testing.md b/.agents/testing.md index b788663..dbe2769 100644 --- a/.agents/testing.md +++ b/.agents/testing.md @@ -52,8 +52,16 @@ helm template t charts/authup --set server.theme.enabled=true --set server.theme helm template t charts/authup --set server.theme.enabled=true --set server.theme.logo=assets/logo.svg # asset missing from files helm template t charts/authup --set server.theme.enabled=true --set 'server.theme.tokens.--authup-bg=url(x)' # token value authup rejects helm template t charts/authup --set 'server.trustedOrigins[0]=https://**.x' # globstar host +helm template t charts/authup --set server.route.enabled=true --set server.publicUrl=https://h.x/auth # sub-path route without matches +helm template t charts/authup --set server.route.enabled=true --set server.ingress.enabled=true \ + --set server.ingress.hostname=h.x --set server.ingress.path=/auth # same, via the derived URL ``` +The route guard reads the public URL AFTER derivation, so the ingress-derived +case needs its own line: only the origin reaches the HTTPRoute hostname, and the +dropped path is exactly what turns the rule into a catch-all. Adding +`--set 'server.route.matches[0].path.value=/auth'` must make both RENDER. + Umbrella use is part of the contract: `global` must stay open. Render a throwaway parent chart with authup in `charts/` and an unrelated global (`global.myOrgKey`) whenever the schema generation changes; `ci/default-values.yaml` carries a stray diff --git a/charts/authup/Chart.yaml b/charts/authup/Chart.yaml index e4b6034..fe19e57 100644 --- a/charts/authup/Chart.yaml +++ b/charts/authup/Chart.yaml @@ -33,3 +33,7 @@ annotations: artifacthub.io/changes: | - kind: fixed description: A parent chart's own global.* keys no longer fail schema validation + - kind: added + description: server.route / adminConsole.route accept matches and filters + - kind: added + description: An HTTPRoute for a sub-path public URL without matches now fails the render diff --git a/charts/authup/README.md b/charts/authup/README.md index d695c2b..e61e792 100644 --- a/charts/authup/README.md +++ b/charts/authup/README.md @@ -187,7 +187,7 @@ Kubernetes: `>=1.25.0-0` | adminConsole.ingress.enabled | bool | `false` | Enable ingress for the UI | | adminConsole.ingress.extraHosts | list | `[]` | Extra hosts | | adminConsole.ingress.extraPaths | list | `[]` | Extra paths for the primary host | -| adminConsole.ingress.extraRules | list | `[]` | Full custom rules (tpl-rendered; overrides the generated rule) | +| adminConsole.ingress.extraRules | list | `[]` | Full custom rules (tpl-rendered; appended after the generated rules) | | adminConsole.ingress.extraTls | list | `[]` | Extra TLS entries | | adminConsole.ingress.hostname | string | `""` | Ingress hostname (tpl-rendered); also drives the derived UI public URL | | adminConsole.ingress.ingressClassName | string | `""` | Ingress class name | @@ -231,7 +231,9 @@ Kubernetes: `>=1.25.0-0` | adminConsole.revisionHistoryLimit | int | `3` | Deployment revision history limit | | adminConsole.route.annotations | object | `{}` | HTTPRoute annotations | | adminConsole.route.enabled | bool | `false` | Create a Gateway API HTTPRoute for the UI | -| adminConsole.route.hostnames | list | `[]` | Route hostnames ([] = derived from adminConsole.publicUrl / ingress hostname) | +| adminConsole.route.filters | list | `[]` | Rule filters (tpl-rendered), e.g. a URLRewrite stripping a path prefix | +| adminConsole.route.hostnames | list | `[]` | Route hostnames ([] = derived from adminConsole.publicUrl / ingress hostname; only the host is kept, a public URL path is dropped and needs its own matches entry) | +| adminConsole.route.matches | list | `[]` | Rule matches (tpl-rendered); [] is the Gateway API default, PathPrefix "/" | | adminConsole.route.parentRefs | list | `[]` | Gateway parentRefs | | adminConsole.schedulerName | string | `""` | Scheduler name | | adminConsole.service.annotations | object | `{}` | Service annotations (tpl-rendered) | @@ -369,7 +371,7 @@ Kubernetes: `>=1.25.0-0` | server.ingress.enabled | bool | `false` | Enable ingress for server-core. NOTE: this also exposes the UNAUTHENTICATED /metrics endpoint publicly — block it at the ingress controller or disable it via server.configuration ("middlewarePrometheus: false") when it is not scraped | | server.ingress.extraHosts | list | `[]` | Extra hosts | | server.ingress.extraPaths | list | `[]` | Extra paths for the primary host | -| server.ingress.extraRules | list | `[]` | Full custom rules (tpl-rendered; overrides the generated rule) | +| server.ingress.extraRules | list | `[]` | Full custom rules (tpl-rendered; appended after the generated rules) | | server.ingress.extraTls | list | `[]` | Extra TLS entries | | server.ingress.hostname | string | `""` | Ingress hostname (tpl-rendered); also drives the derived PUBLIC_URL | | server.ingress.ingressClassName | string | `""` | Ingress class name | @@ -433,7 +435,9 @@ Kubernetes: `>=1.25.0-0` | server.revisionHistoryLimit | int | `3` | Deployment revision history limit | | server.route.annotations | object | `{}` | HTTPRoute annotations | | server.route.enabled | bool | `false` | Create a Gateway API HTTPRoute for server-core | -| server.route.hostnames | list | `[]` | Route hostnames ([] = derived from server.publicUrl / ingress hostname) | +| server.route.filters | list | `[]` | Rule filters (tpl-rendered), e.g. a URLRewrite stripping a path prefix | +| server.route.hostnames | list | `[]` | Route hostnames ([] = derived from server.publicUrl / ingress hostname; only the host is kept, a public URL path is dropped and needs its own matches entry) | +| server.route.matches | list | `[]` | Rule matches (tpl-rendered); [] is the Gateway API default, PathPrefix "/" | | server.route.parentRefs | list | `[]` | Gateway parentRefs | | server.schedulerName | string | `""` | Scheduler name | | server.service.annotations | object | `{}` | Service annotations (tpl-rendered) | diff --git a/charts/authup/templates/_ingress.tpl b/charts/authup/templates/_ingress.tpl index ca08d28..4d75ce7 100644 --- a/charts/authup/templates/_ingress.tpl +++ b/charts/authup/templates/_ingress.tpl @@ -113,4 +113,10 @@ spec: - backendRefs: - name: {{ .serviceName }} port: {{ .servicePort }} + {{- if $route.matches }} + matches: {{- include "authup.tplvalues.render" (dict "value" $route.matches "context" $ctx) | nindent 8 }} + {{- end }} + {{- if $route.filters }} + filters: {{- include "authup.tplvalues.render" (dict "value" $route.filters "context" $ctx) | nindent 8 }} + {{- end }} {{- end -}} diff --git a/charts/authup/templates/validations.yaml b/charts/authup/templates/validations.yaml index dbbbed6..ffac23c 100644 --- a/charts/authup/templates/validations.yaml +++ b/charts/authup/templates/validations.yaml @@ -50,6 +50,26 @@ store, token blocklist and MFA challenges fall back to a per-process memory cach {{- fail "authup: adminConsole.ingress.enabled requires adminConsole.ingress.hostname (or extraHosts / extraRules)." }} {{- end }} +{{/* +A Gateway API HTTPRoute rule without `matches` defaults to PathPrefix "/", i.e. it +matches every request on every hostname the route attaches to. The route hostname is +derived from the public URL's ORIGIN, so a sub-path public URL would silently attach +authup as the catch-all backend of a hostname it is meant to share. authup always +serves at "/" and expects the proxy to strip the prefix, hence match AND rewrite. +*/}} +{{- if and .Values.server.enabled .Values.server.route.enabled (not .Values.server.route.matches) }} +{{- $path := (urlParse (include "authup.server.publicUrl" .)).path }} +{{- if and $path (ne $path "/") }} +{{- fail (printf "authup: the server public URL carries the path %q but server.route.matches is empty, which the Gateway API defaults to PathPrefix \"/\" (a catch-all on every hostname this route attaches to). authup serves at \"/\" and expects the proxy to strip the prefix, so set both:\n server.route.matches: [{path: {type: PathPrefix, value: %s}}]\n server.route.filters: [{type: URLRewrite, urlRewrite: {path: {type: ReplacePrefixMatch, replacePrefixMatch: /}}}]" $path $path) }} +{{- end }} +{{- end }} +{{- if and .Values.adminConsole.enabled .Values.adminConsole.route.enabled (not .Values.adminConsole.route.matches) }} +{{- $path := (urlParse (include "authup.adminConsole.publicUrl" .)).path }} +{{- if and $path (ne $path "/") }} +{{- fail (printf "authup: the admin console public URL carries the path %q but adminConsole.route.matches is empty, which the Gateway API defaults to PathPrefix \"/\" (a catch-all on every hostname this route attaches to). Set both:\n adminConsole.route.matches: [{path: {type: PathPrefix, value: %s}}]\n adminConsole.route.filters: [{type: URLRewrite, urlRewrite: {path: {type: ReplacePrefixMatch, replacePrefixMatch: /}}}]" $path $path) }} +{{- end }} +{{- end }} + {{/* URL values must carry a scheme — a scheme-less value would derive a broken "://" origin into TRUSTED_ORIGINS and crash-loop server-core. */}} {{- range $key, $value := dict "server.publicUrl" .Values.server.publicUrl "adminConsole.publicUrl" .Values.adminConsole.publicUrl "adminConsole.apiUrl" .Values.adminConsole.apiUrl "adminConsole.internalApiUrl" .Values.adminConsole.internalApiUrl }} {{- if and $value (not (contains "{{" $value)) (not (regexMatch "^https?://" $value)) }} diff --git a/charts/authup/values.schema.json b/charts/authup/values.schema.json index 332d332..608aa7f 100644 --- a/charts/authup/values.schema.json +++ b/charts/authup/values.schema.json @@ -312,7 +312,7 @@ "type": "array" }, "extraRules": { - "description": "Full custom rules (tpl-rendered; overrides the generated rule)", + "description": "Full custom rules (tpl-rendered; appended after the generated rules)", "items": { "required": [] }, @@ -742,8 +742,17 @@ "title": "enabled", "type": "boolean" }, + "filters": { + "description": "Rule filters (tpl-rendered), e.g. a URLRewrite stripping a path prefix", + "items": { + "required": [] + }, + "required": [], + "title": "filters", + "type": "array" + }, "hostnames": { - "description": "Route hostnames ([] = derived from adminConsole.publicUrl / ingress hostname)", + "description": "Route hostnames ([] = derived from adminConsole.publicUrl / ingress hostname; only the host is kept, a public\nURL path is dropped and needs its own matches entry)", "items": { "required": [] }, @@ -751,6 +760,15 @@ "title": "hostnames", "type": "array" }, + "matches": { + "description": "Rule matches (tpl-rendered); [] is the Gateway API default, PathPrefix \"/\"", + "items": { + "required": [] + }, + "required": [], + "title": "matches", + "type": "array" + }, "parentRefs": { "description": "Gateway parentRefs", "items": { @@ -764,7 +782,9 @@ "required": [ "enabled", "hostnames", - "parentRefs" + "parentRefs", + "matches", + "filters" ], "title": "route", "type": "object" @@ -2469,7 +2489,7 @@ "type": "array" }, "extraRules": { - "description": "Full custom rules (tpl-rendered; overrides the generated rule)", + "description": "Full custom rules (tpl-rendered; appended after the generated rules)", "items": { "required": [] }, @@ -3095,8 +3115,17 @@ "title": "enabled", "type": "boolean" }, + "filters": { + "description": "Rule filters (tpl-rendered), e.g. a URLRewrite stripping a path prefix", + "items": { + "required": [] + }, + "required": [], + "title": "filters", + "type": "array" + }, "hostnames": { - "description": "Route hostnames ([] = derived from server.publicUrl / ingress hostname)", + "description": "Route hostnames ([] = derived from server.publicUrl / ingress hostname; only the host is kept, a public\nURL path is dropped and needs its own matches entry)", "items": { "required": [] }, @@ -3104,6 +3133,15 @@ "title": "hostnames", "type": "array" }, + "matches": { + "description": "Rule matches (tpl-rendered); [] is the Gateway API default, PathPrefix \"/\"", + "items": { + "required": [] + }, + "required": [], + "title": "matches", + "type": "array" + }, "parentRefs": { "description": "Gateway parentRefs", "items": { @@ -3117,7 +3155,9 @@ "required": [ "enabled", "hostnames", - "parentRefs" + "parentRefs", + "matches", + "filters" ], "title": "route", "type": "object" diff --git a/charts/authup/values.yaml b/charts/authup/values.yaml index 17a10b3..3fe6741 100644 --- a/charts/authup/values.yaml +++ b/charts/authup/values.yaml @@ -743,12 +743,13 @@ server: extraPaths: [] # -- Extra TLS entries extraTls: [] - # -- Full custom rules (tpl-rendered; overrides the generated rule) + # -- Full custom rules (tpl-rendered; appended after the generated rules) extraRules: [] route: # -- Create a Gateway API HTTPRoute for server-core enabled: false - # -- Route hostnames ([] = derived from server.publicUrl / ingress hostname) + # -- Route hostnames ([] = derived from server.publicUrl / ingress hostname; only the host is kept, a public + # URL path is dropped and needs its own matches entry) hostnames: [] # -- Gateway parentRefs parentRefs: [] @@ -757,6 +758,10 @@ server: # @schema # -- HTTPRoute annotations annotations: {} + # -- Rule matches (tpl-rendered); [] is the Gateway API default, PathPrefix "/" + matches: [] + # -- Rule filters (tpl-rendered), e.g. a URLRewrite stripping a path prefix + filters: [] metrics: # -- The /metrics endpoint is UNAUTHENTICATED; keep it off the public ingress serviceMonitor: @@ -1044,12 +1049,13 @@ adminConsole: extraPaths: [] # -- Extra TLS entries extraTls: [] - # -- Full custom rules (tpl-rendered; overrides the generated rule) + # -- Full custom rules (tpl-rendered; appended after the generated rules) extraRules: [] route: # -- Create a Gateway API HTTPRoute for the UI enabled: false - # -- Route hostnames ([] = derived from adminConsole.publicUrl / ingress hostname) + # -- Route hostnames ([] = derived from adminConsole.publicUrl / ingress hostname; only the host is kept, a public + # URL path is dropped and needs its own matches entry) hostnames: [] # -- Gateway parentRefs parentRefs: [] @@ -1058,6 +1064,10 @@ adminConsole: # @schema # -- HTTPRoute annotations annotations: {} + # -- Rule matches (tpl-rendered); [] is the Gateway API default, PathPrefix "/" + matches: [] + # -- Rule filters (tpl-rendered), e.g. a URLRewrite stripping a path prefix + filters: [] pdb: # -- Create a PodDisruptionBudget for the UI create: false From 93c249497194c8368ab5b78460e95df0bcb0d36f Mon Sep 17 00:00:00 2001 From: tada5hi Date: Wed, 19 Aug 2026 12:00:42 +0200 Subject: [PATCH 3/3] docs(authup): note that a path-prefixed route needs the prefix stripped The render guard only fires when route.matches is empty, so an operator who sets matches from the start never sees the message that names the URLRewrite. authup always serves at "/" and relies on something upstream stripping the prefix, and getting that wrong 404s every request instead of failing the render. Left as a NOTE rather than a validation: the stripping may legitimately happen outside this HTTPRoute (an edge proxy or ingress in front of the gateway, or an implementation-specific filter such as an NGINX Gateway Fabric SnippetsFilter), so requiring a URLRewrite filter here would fail renders that are correct. --- charts/authup/templates/NOTES.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/charts/authup/templates/NOTES.txt b/charts/authup/templates/NOTES.txt index b6c6d42..19a529e 100644 --- a/charts/authup/templates/NOTES.txt +++ b/charts/authup/templates/NOTES.txt @@ -98,6 +98,22 @@ small deployments. For production, consider an external database (externalDatabase.*) or an operator (e.g. CloudNativePG). {{- end }} +{{- $prefixed := list }} +{{- if and .Values.server.enabled .Values.server.route.enabled (urlParse $serverUrl).path (ne (urlParse $serverUrl).path "/") }} +{{- $prefixed = append $prefixed (printf "server.route %s" (urlParse $serverUrl).path) }} +{{- end }} +{{- if and .Values.adminConsole.enabled .Values.adminConsole.route.enabled (urlParse $uiUrl).path (ne (urlParse $uiUrl).path "/") }} +{{- $prefixed = append $prefixed (printf "adminConsole.route %s" (urlParse $uiUrl).path) }} +{{- end }} +{{- if $prefixed }} + +NOTE: serving under a path prefix ({{ join ", " $prefixed }}). authup itself always +serves at "/", so something in the chain has to strip the prefix: a route.filters +URLRewrite (ReplacePrefixMatch "/"), an implementation-specific filter, or a proxy +in front of the gateway. Without one, every request reaches the pod with the prefix +still attached and 404s. +{{- end }} + {{- if $serverUrl }} Changing PUBLIC_URL later breaks enrolled WebAuthn credentials (the rpId binds