From 13173ad645359bbe0a7dcd3dabc3f9845a579e53 Mon Sep 17 00:00:00 2001 From: tada5hi Date: Sun, 2 Aug 2026 13:37:54 +0200 Subject: [PATCH 1/5] refactor(authup)!: follow the client-web to client-console app rename authup/authup#3370 renames the admin UI app: the docker entrypoint selector changes from client/web to client/console. The ui deployment args and every doc/comment reference follow. The server:/ui: values keys are unchanged (operator-facing vocabulary). Must ship together with the appVersion bump to the first authup release that contains the rename; the beta.58 image only knows client/web. --- .agents/architecture.md | 2 +- .agents/references/authup.md | 10 +++++----- .agents/structure.md | 6 +++--- AGENTS.md | 2 +- DESIGN.md | 10 +++++----- README.md | 4 ++-- charts/authup/Chart.yaml | 2 +- charts/authup/README.md | 8 ++++---- charts/authup/templates/NOTES.txt | 2 +- charts/authup/templates/_ui-env.tpl | 4 ++-- charts/authup/templates/ui/deployment.yaml | 2 +- charts/authup/values.yaml | 6 +++--- 12 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.agents/architecture.md b/.agents/architecture.md index d825eab..cfffbc7 100644 --- a/.agents/architecture.md +++ b/.agents/architecture.md @@ -76,7 +76,7 @@ editing templates or values. ingress blocks; the UI origin is auto-appended to `TRUSTED_ORIGINS` (`server.trustedOriginsAppendUI`). A missing trusted origin is the #1 dead-login misconfiguration. The chart never sets - `NUXT_PUBLIC_COOKIE_DOMAIN`: sharing a cookie domain between client-web + `NUXT_PUBLIC_COOKIE_DOMAIN`: sharing a cookie domain between client-console and the hosted auth pages is unsupported by authup. 16. **Every list/map passthrough is tpl-rendered** via `authup.tplvalues.render`, so umbrella charts can inject template diff --git a/.agents/references/authup.md b/.agents/references/authup.md index 47bcfc3..d714334 100644 --- a/.agents/references/authup.md +++ b/.agents/references/authup.md @@ -9,7 +9,7 @@ v1.0.0-beta.58 line (chart `appVersion`). | Fact | authup source | Chart counterpart | |---|---|---| -| One image `authup/authup`, arg-dispatched entrypoint (`server/core start`, `client/web start`, `server/core migration run`, `server/core healthcheck`) | `Dockerfile`, `entrypoint.sh` (repo root) | `args` in `templates/{server,ui}/deployment.yaml`, `server/migration-job.yaml` | +| One image `authup/authup`, arg-dispatched entrypoint (`server/core start`, `client/console start`, `server/core migration run`, `server/core healthcheck`) | `Dockerfile`, `entrypoint.sh` (repo root) | `args` in `templates/{server,ui}/deployment.yaml`, `server/migration-job.yaml` | | Entrypoint force-exports `PORT=3000` / `NUXT_PORT=3000` (chart-set PORT is dead) | `entrypoint.sh` | containerPort pinned 3000 everywhere | | Image runs as root; writable paths `/usr/src/app/writable` + npm cache | `Dockerfile` (`WRITABLE_DIRECTORY_PATH`, no `USER`) | emptyDir mounts + `npm_config_cache=/tmp/.npm-cache`; root securityContext default | | `latest`/``/`beta`/`next` tags | `.github/workflows/release.yml`, `docker-nightly.yml` | `image.tag` defaults to `Chart.AppVersion` | @@ -39,11 +39,11 @@ Config file: `authup.server.core.conf` in the process cwd `/provisioning/*` scanned at boot, fail-closed (`app/modules/provisioning/module.ts`) -> `server.provisioning.*` mount. -## client-web env surface +## client-console env surface Runtime config only (prebuilt Nitro bundle; bare `API_URL` etc. are build-time -and dead): `apps/client-web/nuxt.config.ts`, -`docs/src/guide/deployment/configuration-client-web.md`. +and dead): `apps/client-console/nuxt.config.ts`, +`docs/src/guide/deployment/configuration-client-console.md`. `NUXT_PUBLIC_API_URL` (browser-reachable server URL), `NUXT_PUBLIC_PUBLIC_URL`, `NUXT_API_URL` (SSR-side override), `NUXT_PUBLIC_COOKIE_DOMAIN` (deliberately never set by the chart: sharing a cookie domain with the server origin is @@ -54,7 +54,7 @@ unsupported per `.agents/architecture.md` in the monorepo). Chart counterpart: - `GET /` = anonymous status endpoint `{version, date, features}` (`adapters/http/controllers/workflows/status/`) -> liveness/readiness for - server-core; client-web uses its SSR `/`. + server-core; client-console uses its SSR `/`. - server-core auto-runs migrations + provisioning at boot (`app/modules/database/module.ts`; no off-switch) -> generous startupProbe; optional pre-upgrade migration Job for multi-replica DDL serialization. diff --git a/.agents/structure.md b/.agents/structure.md index b74ee89..fc2b612 100644 --- a/.agents/structure.md +++ b/.agents/structure.md @@ -47,7 +47,7 @@ authup/helm ├── server/ # server-core: deployment, service, ingress, httproute, │ # configmap-env, configmap-configuration, configmap-provisioning, │ # migration-job, hpa, pdb, networkpolicy, servicemonitor - ├── ui/ # client-web: deployment, service, ingress, httproute, + ├── ui/ # client-console: deployment, service, ingress, httproute, │ # configmap-env, hpa, pdb, networkpolicy ├── postgresql/ # built-in instance: statefulset, service, secret ├── mysql/ # built-in instance: statefulset, service, secret @@ -57,14 +57,14 @@ authup/helm ## Two components, per-role template directories `server/` (server-core, the IdP: OAuth2/OIDC surface + SSR auth pages) and -`ui/` (client-web admin console) are separate template directories with ~85% +`ui/` (client-console admin console) are separate template directories with ~85% similar deployment templates. This duplication is DELIBERATE: authentik built the DRY role-loop and reverted it ("takes DRY maybe a bit too far", their PR #163). Do not introduce a role loop. A future authup server/worker split becomes a third directory with the same skeleton. Both services run the SAME image (`authup/authup`) with different args -(`server/core start` vs `client/web start`). The image entrypoint force-exports +(`server/core start` vs `client/console start`). The image entrypoint force-exports `PORT=3000` for both, so `containerPort` is pinned to 3000 everywhere and only Service ports are values. diff --git a/AGENTS.md b/AGENTS.md index 506eaa9..da73691 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ Helm charts for [Authup](https://authup.org), an authentication & authorization system. One application chart today: `charts/authup` deploys the two runtime services of the [authup monorepo](https://github.com/authup/authup) (server-core -IdP/API and the client-web admin UI) plus optional built-in PostgreSQL, MySQL +IdP/API and the client-console admin UI) plus optional built-in PostgreSQL, MySQL and Valkey instances. `DESIGN.md` at the repo root is the authoritative design record: every major diff --git a/DESIGN.md b/DESIGN.md index bfa30cf..074780e 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -23,7 +23,7 @@ repo; every decision below cites its evidence. vendored locally (Apache-2.0-clean, bitnami-compatible key names). 2. **Two workloads, one chart, one image.** `authup/authup` is a single image whose entrypoint dispatches on args: `server/core start` (server-core, the IdP/API) and - `client/web start` (client-web, the Nuxt admin UI). The `authup` CLI supervisor is + `client/console start` (client-console, the Nuxt admin UI). The `authup` CLI supervisor is **not routable through the container entrypoint** and the monorepo docs pin "containers with one service each" as the production topology — so the chart ships two Deployments and never a combined pod. @@ -76,7 +76,7 @@ authup/helm │ ├── server/ # server-core: deployment, service, ingress, │ │ # httproute, configmap-env, migration-job, hpa, │ │ # pdb, networkpolicy, servicemonitor -│ ├── ui/ # client-web: deployment, service, ingress, +│ ├── ui/ # client-console: deployment, service, ingress, │ │ # httproute, configmap-env, hpa, pdb, networkpolicy │ ├── postgresql/ # optional built-in dev instance (statefulset, │ │ # service, secret) — docker-official image @@ -100,9 +100,9 @@ charts (e.g. an `authup-remote` RBAC chart, authentik-style). ### 3.1 Workloads -| | `server` (server-core) | `ui` (client-web) | +| | `server` (server-core) | `ui` (client-console) | |---|---|---| -| args | `["server/core", "start"]` | `["client/web", "start"]` | +| args | `["server/core", "start"]` | `["client/console", "start"]` | | containerPort | 3000 (pinned) | 3000 (pinned) | | role | OAuth2/OIDC IdP origin + SSR auth pages | admin console, ordinary OAuth2 RP | | state | stateless w/ external DB+redis | fully stateless | @@ -274,7 +274,7 @@ externalRedis: ### 3.7 Ingress and topology Two-host model as the default (server-core is the IdP origin serving the SSR auth -pages; client-web is an ordinary RP; **cookie-domain sharing between the two is +pages; client-console is an ordinary RP; **cookie-domain sharing between the two is unsupported by authup** — the chart never sets `NUXT_PUBLIC_COOKIE_DOMAIN` and validates against foot-guns): diff --git a/README.md b/README.md index 5b979a9..735dea9 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ on Kubernetes. ## Highlights - 🔐 **Complete deployment** - the server-core IdP/API (OAuth2 / OpenID Connect, - hosted login & consent pages) and the client-web admin UI, from one chart + hosted login & consent pages) and the client-console admin UI, from one chart - 🗄️ **Hybrid database model** - built-in PostgreSQL **or** MySQL for a one-command start, or bring your own external database - ⚡ **Optional Valkey cache** - built-in instance or external Redis; required @@ -100,7 +100,7 @@ key). | Chart | Description | |---|---| -| [authup](./charts/authup) | server-core (IdP/API) + client-web (admin UI), optional built-in PostgreSQL / MySQL / Valkey | +| [authup](./charts/authup) | server-core (IdP/API) + client-console (admin UI), optional built-in PostgreSQL / MySQL / Valkey | ## Documentation diff --git a/charts/authup/Chart.yaml b/charts/authup/Chart.yaml index c6d1db3..88c22f2 100644 --- a/charts/authup/Chart.yaml +++ b/charts/authup/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: authup -description: Authup is an authentication & authorization system. This chart deploys the server-core IdP/API service and the client-web admin UI, with optional built-in PostgreSQL, MySQL and Valkey instances. +description: Authup is an authentication & authorization system. This chart deploys the server-core IdP/API service and the client-console admin UI, with optional built-in PostgreSQL, MySQL and Valkey instances. type: application version: 0.1.0 appVersion: "1.0.0-beta.58" diff --git a/charts/authup/README.md b/charts/authup/README.md index 6e7a5be..6bae492 100644 --- a/charts/authup/README.md +++ b/charts/authup/README.md @@ -8,12 +8,12 @@ ![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.0.0-beta.58](https://img.shields.io/badge/AppVersion-1.0.0--beta.58-informational?style=flat-square) -Authup is an authentication & authorization system. This chart deploys the server-core IdP/API service and the client-web admin UI, with optional built-in PostgreSQL, MySQL and Valkey instances. It deploys: +Authup is an authentication & authorization system. This chart deploys the server-core IdP/API service and the client-console admin UI, with optional built-in PostgreSQL, MySQL and Valkey instances. It deploys: - **server-core** — the Authup IdP/API service: the OAuth2/OIDC protocol surface plus the server-rendered auth pages (login, consent, registration, password recovery). This is the identity origin. -- **client-web** — the Nuxt-based admin UI, an ordinary OAuth2 relying party +- **client-console** — the Nuxt-based admin UI, an ordinary OAuth2 relying party (optional; disable with `ui.enabled=false` for a headless IdP). - optionally, single-instance **PostgreSQL**, **MySQL** or **Valkey** built-in instances on docker-official images — a convenience for dev and small @@ -313,7 +313,7 @@ Kubernetes: `>=1.25.0-0` | server.topologySpreadConstraints | list | `[]` | Topology spread constraints (a missing labelSelector is filled with the pod's selector labels) | | server.trustProxy | string | `"1"` | TRUST_PROXY setting. The chart defaults to one trusted hop (the ingress), not authup's spoofable trust-everything default | | server.trustedOrigins | list | `[]` | Additional trusted first-party app origins (TRUSTED_ORIGINS). Each listed origin can obtain full-permission tokens via the per-realm web client. List or comma-separated string; tpl-rendered. | -| server.trustedOriginsAppendUI | bool | `true` | Automatically append the client-web UI origin to TRUSTED_ORIGINS (removes the most common dead-login misconfiguration) | +| server.trustedOriginsAppendUI | bool | `true` | Automatically append the client-console UI origin to TRUSTED_ORIGINS (removes the most common dead-login misconfiguration) | | server.updateStrategy | object | `{"type":"RollingUpdate"}` | Deployment update strategy | | serviceAccount.annotations | object | `{}` | ServiceAccount annotations (tpl-rendered) | | serviceAccount.automountServiceAccountToken | bool | `false` | Automount the service account token | @@ -337,7 +337,7 @@ Kubernetes: `>=1.25.0-0` | ui.customReadinessProbe | object | `{}` | Custom readiness probe | | ui.customStartupProbe | object | `{}` | Custom startup probe | | ui.disableRestartOnChanges | bool | `false` | Disable the checksum annotations that roll pods on config changes | -| ui.enabled | bool | `true` | Deploy the client-web admin UI (false = headless IdP) | +| ui.enabled | bool | `true` | Deploy the client-console admin UI (false = headless IdP) | | ui.extraEnvVars | list | `[]` | Extra environment variables for the UI container | | ui.extraEnvVarsCM | string | `""` | Extra ConfigMap with environment variables (tpl-rendered name) | | ui.extraEnvVarsSecret | string | `""` | Extra Secret with environment variables (tpl-rendered name) | diff --git a/charts/authup/templates/NOTES.txt b/charts/authup/templates/NOTES.txt index b2ef951..9d62a77 100644 --- a/charts/authup/templates/NOTES.txt +++ b/charts/authup/templates/NOTES.txt @@ -27,7 +27,7 @@ server-core (IdP / API): {{- if .Values.ui.enabled }} -client-web (admin UI): +client-console (admin UI): {{- if $uiUrl }} URL: {{ $uiUrl }} {{- else }} diff --git a/charts/authup/templates/_ui-env.tpl b/charts/authup/templates/_ui-env.tpl index 397ecb0..01eda0c 100644 --- a/charts/authup/templates/_ui-env.tpl +++ b/charts/authup/templates/_ui-env.tpl @@ -1,9 +1,9 @@ {{/* -client-web environment as a YAML map. The published UI bundle only honors Nuxt +client-console environment as a YAML map. The published UI bundle only honors Nuxt runtime-config names (NUXT_*); the API URL must be the BROWSER-reachable server-core URL, never a cluster-internal service name. The chart deliberately never sets NUXT_PUBLIC_COOKIE_DOMAIN: sharing a cookie -domain between client-web and the hosted auth pages is unsupported by authup. +domain between client-console and the hosted auth pages is unsupported by authup. */}} {{- define "authup.ui.configEnv" -}} {{- $apiUrl := include "authup.ui.apiUrl" . }} diff --git a/charts/authup/templates/ui/deployment.yaml b/charts/authup/templates/ui/deployment.yaml index 0cdc297..bcb2e3d 100644 --- a/charts/authup/templates/ui/deployment.yaml +++ b/charts/authup/templates/ui/deployment.yaml @@ -81,7 +81,7 @@ spec: args: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.args "context" $) | nindent 12 }} {{- else }} args: - - client/web + - client/console - start {{- end }} {{- end }} diff --git a/charts/authup/values.yaml b/charts/authup/values.yaml index c40d510..b208577 100644 --- a/charts/authup/values.yaml +++ b/charts/authup/values.yaml @@ -406,7 +406,7 @@ server: # origin can obtain full-permission tokens via the per-realm web client. # List or comma-separated string; tpl-rendered. trustedOrigins: [] - # -- Automatically append the client-web UI origin to TRUSTED_ORIGINS (removes the + # -- Automatically append the client-console UI origin to TRUSTED_ORIGINS (removes the # most common dead-login misconfiguration) trustedOriginsAppendUI: true # -- TRUST_PROXY setting. The chart defaults to one trusted hop (the ingress), @@ -757,10 +757,10 @@ server: # -- Extra egress rules extraEgress: [] -## @section client-web (admin UI) +## @section client-console (admin UI) ui: - # -- Deploy the client-web admin UI (false = headless IdP) + # -- Deploy the client-console admin UI (false = headless IdP) enabled: true # -- Number of UI replicas (fully stateless, scale freely) replicaCount: 1 From 472f18945a6bd81d44da8fbbb7d710580208859b Mon Sep 17 00:00:00 2001 From: tada5hi Date: Sun, 2 Aug 2026 15:27:40 +0200 Subject: [PATCH 2/5] fix(authup): rename client-web in helm-docs template and generated schema The lint job regenerates README.md and values.schema.json; the README.md.gotmpl source and the generated schema still carried client-web, so the drift check re-introduced it. --- charts/authup/README.md.gotmpl | 2 +- charts/authup/values.schema.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/authup/README.md.gotmpl b/charts/authup/README.md.gotmpl index cc02c22..36eeb63 100644 --- a/charts/authup/README.md.gotmpl +++ b/charts/authup/README.md.gotmpl @@ -15,7 +15,7 @@ - **server-core** — the Authup IdP/API service: the OAuth2/OIDC protocol surface plus the server-rendered auth pages (login, consent, registration, password recovery). This is the identity origin. -- **client-web** — the Nuxt-based admin UI, an ordinary OAuth2 relying party +- **client-console** — the Nuxt-based admin UI, an ordinary OAuth2 relying party (optional; disable with `ui.enabled=false` for a headless IdP). - optionally, single-instance **PostgreSQL**, **MySQL** or **Valkey** built-in instances on docker-official images — a convenience for dev and small diff --git a/charts/authup/values.schema.json b/charts/authup/values.schema.json index 9ea4d20..2185609 100644 --- a/charts/authup/values.schema.json +++ b/charts/authup/values.schema.json @@ -2311,7 +2311,7 @@ }, "trustedOriginsAppendUI": { "default": true, - "description": "Automatically append the client-web UI origin to TRUSTED_ORIGINS (removes the\nmost common dead-login misconfiguration)", + "description": "Automatically append the client-console UI origin to TRUSTED_ORIGINS (removes the\nmost common dead-login misconfiguration)", "required": [], "title": "trustedOriginsAppendUI", "type": "boolean" @@ -2665,7 +2665,7 @@ }, "enabled": { "default": true, - "description": "Deploy the client-web admin UI (false = headless IdP)", + "description": "Deploy the client-console admin UI (false = headless IdP)", "required": [], "title": "enabled", "type": "boolean" From 71ca6cf178dedda71a95070b056df397f4a8d1b1 Mon Sep 17 00:00:00 2001 From: tada5hi Date: Sun, 2 Aug 2026 18:12:36 +0200 Subject: [PATCH 3/5] refactor(authup)!: finalize admin UI naming (client-admin-console, adminConsole values) Follows the amended upstream rename (authup/authup#3370, merged): the app is client-admin-console and the docker selector client/admin-console. Values section ui.* renames to adminConsole.*, and server.trustedOriginsAppendUI to server.trustedOriginsAppendAdminConsole. Template dir templates/ui/ becomes templates/admin-console/, helper names and the component label follow, and rendered resource names change suffix -ui to -admin-console. Value migrations recorded in BREAKING.md (0.2.0). Ship together with the appVersion bump to the first authup release containing the rename; older images only know client/web. --- .agents/architecture.md | 4 +- .agents/references/authup.md | 12 +- .agents/references/goauthentik-helm.md | 2 +- .agents/structure.md | 10 +- .agents/testing.md | 2 +- AGENTS.md | 2 +- DESIGN.md | 20 +- README.md | 4 +- charts/authup/BREAKING.md | 19 +- charts/authup/Chart.yaml | 2 +- charts/authup/README.md | 200 +- charts/authup/README.md.gotmpl | 4 +- charts/authup/ci/default-values.yaml | 2 +- charts/authup/ci/external-db-values.yaml | 2 +- charts/authup/ci/mysql-values.yaml | 2 +- charts/authup/ci/server-only-values.yaml | 2 +- charts/authup/ci/valkey-values.yaml | 2 +- charts/authup/templates/NOTES.txt | 16 +- .../{_ui-env.tpl => _admin-console-env.tpl} | 16 +- charts/authup/templates/_helpers.tpl | 4 +- charts/authup/templates/_urls.tpl | 20 +- .../{ui => admin-console}/configmap-env.yaml | 8 +- .../templates/admin-console/deployment.yaml | 159 + .../authup/templates/admin-console/hpa.yaml | 33 + .../templates/admin-console/httproute.yaml | 11 + .../templates/admin-console/ingress.yaml | 9 + .../{ui => admin-console}/networkpolicy.yaml | 28 +- .../authup/templates/admin-console/pdb.yaml | 20 + .../templates/admin-console/service.yaml | 47 + .../templates/server/networkpolicy.yaml | 2 +- charts/authup/templates/ui/deployment.yaml | 159 - charts/authup/templates/ui/hpa.yaml | 33 - charts/authup/templates/ui/httproute.yaml | 11 - charts/authup/templates/ui/ingress.yaml | 9 - charts/authup/templates/ui/pdb.yaml | 20 - charts/authup/templates/ui/service.yaml | 47 - charts/authup/templates/validations.yaml | 10 +- charts/authup/values.schema.json | 4052 ++++++++--------- charts/authup/values.yaml | 14 +- 39 files changed, 2518 insertions(+), 2501 deletions(-) rename charts/authup/templates/{_ui-env.tpl => _admin-console-env.tpl} (53%) rename charts/authup/templates/{ui => admin-console}/configmap-env.yaml (54%) create mode 100644 charts/authup/templates/admin-console/deployment.yaml create mode 100644 charts/authup/templates/admin-console/hpa.yaml create mode 100644 charts/authup/templates/admin-console/httproute.yaml create mode 100644 charts/authup/templates/admin-console/ingress.yaml rename charts/authup/templates/{ui => admin-console}/networkpolicy.yaml (58%) create mode 100644 charts/authup/templates/admin-console/pdb.yaml create mode 100644 charts/authup/templates/admin-console/service.yaml delete mode 100644 charts/authup/templates/ui/deployment.yaml delete mode 100644 charts/authup/templates/ui/hpa.yaml delete mode 100644 charts/authup/templates/ui/httproute.yaml delete mode 100644 charts/authup/templates/ui/ingress.yaml delete mode 100644 charts/authup/templates/ui/pdb.yaml delete mode 100644 charts/authup/templates/ui/service.yaml diff --git a/.agents/architecture.md b/.agents/architecture.md index cfffbc7..be8b291 100644 --- a/.agents/architecture.md +++ b/.agents/architecture.md @@ -74,9 +74,9 @@ editing templates or values. 15. **URL derivation is the chart's core UX.** `PUBLIC_URL`, `NUXT_PUBLIC_API_URL`, `NUXT_PUBLIC_PUBLIC_URL` derive from the two ingress blocks; the UI origin is auto-appended to `TRUSTED_ORIGINS` - (`server.trustedOriginsAppendUI`). A missing trusted origin is the #1 + (`server.trustedOriginsAppendAdminConsole`). A missing trusted origin is the #1 dead-login misconfiguration. The chart never sets - `NUXT_PUBLIC_COOKIE_DOMAIN`: sharing a cookie domain between client-console + `NUXT_PUBLIC_COOKIE_DOMAIN`: sharing a cookie domain between client-admin-console and the hosted auth pages is unsupported by authup. 16. **Every list/map passthrough is tpl-rendered** via `authup.tplvalues.render`, so umbrella charts can inject template diff --git a/.agents/references/authup.md b/.agents/references/authup.md index d714334..f9d9708 100644 --- a/.agents/references/authup.md +++ b/.agents/references/authup.md @@ -9,7 +9,7 @@ v1.0.0-beta.58 line (chart `appVersion`). | Fact | authup source | Chart counterpart | |---|---|---| -| One image `authup/authup`, arg-dispatched entrypoint (`server/core start`, `client/console start`, `server/core migration run`, `server/core healthcheck`) | `Dockerfile`, `entrypoint.sh` (repo root) | `args` in `templates/{server,ui}/deployment.yaml`, `server/migration-job.yaml` | +| One image `authup/authup`, arg-dispatched entrypoint (`server/core start`, `client/admin-console start`, `server/core migration run`, `server/core healthcheck`) | `Dockerfile`, `entrypoint.sh` (repo root) | `args` in `templates/{server,admin-console}/deployment.yaml`, `server/migration-job.yaml` | | Entrypoint force-exports `PORT=3000` / `NUXT_PORT=3000` (chart-set PORT is dead) | `entrypoint.sh` | containerPort pinned 3000 everywhere | | Image runs as root; writable paths `/usr/src/app/writable` + npm cache | `Dockerfile` (`WRITABLE_DIRECTORY_PATH`, no `USER`) | emptyDir mounts + `npm_config_cache=/tmp/.npm-cache`; root securityContext default | | `latest`/``/`beta`/`next` tags | `.github/workflows/release.yml`, `docker-nightly.yml` | `image.tag` defaults to `Chart.AppVersion` | @@ -39,22 +39,22 @@ Config file: `authup.server.core.conf` in the process cwd `/provisioning/*` scanned at boot, fail-closed (`app/modules/provisioning/module.ts`) -> `server.provisioning.*` mount. -## client-console env surface +## client-admin-console env surface Runtime config only (prebuilt Nitro bundle; bare `API_URL` etc. are build-time -and dead): `apps/client-console/nuxt.config.ts`, -`docs/src/guide/deployment/configuration-client-console.md`. +and dead): `apps/client-admin-console/nuxt.config.ts`, +`docs/src/guide/deployment/configuration-client-admin-console.md`. `NUXT_PUBLIC_API_URL` (browser-reachable server URL), `NUXT_PUBLIC_PUBLIC_URL`, `NUXT_API_URL` (SSR-side override), `NUXT_PUBLIC_COOKIE_DOMAIN` (deliberately never set by the chart: sharing a cookie domain with the server origin is unsupported per `.agents/architecture.md` in the monorepo). Chart counterpart: -`_ui-env.tpl`. +`_admin-console-env.tpl`. ## Operational contract - `GET /` = anonymous status endpoint `{version, date, features}` (`adapters/http/controllers/workflows/status/`) -> liveness/readiness for - server-core; client-console uses its SSR `/`. + server-core; client-admin-console uses its SSR `/`. - server-core auto-runs migrations + provisioning at boot (`app/modules/database/module.ts`; no off-switch) -> generous startupProbe; optional pre-upgrade migration Job for multi-replica DDL serialization. diff --git a/.agents/references/goauthentik-helm.md b/.agents/references/goauthentik-helm.md index c496519..7eba838 100644 --- a/.agents/references/goauthentik-helm.md +++ b/.agents/references/goauthentik-helm.md @@ -9,7 +9,7 @@ auth server). | Their piece | This repo | |---|---| -| Per-role template directories, duplication accepted (they built and REVERTED the DRY role-loop in #163: "takes DRY maybe a bit too far") | `templates/server/` + `templates/ui/` | +| Per-role template directories, duplication accepted (they built and REVERTED the DRY role-loop in #163: "takes DRY maybe a bit too far") | `templates/server/` + `templates/admin-console/` | | Self-contained chart, no library dependency (their k8s-at-home common era died upstream) | zero `dependencies:` in Chart.yaml | | ct lint + kind `ct install` gated by `ct list-changed`, `ci/*-values.yaml` scenario matrix, `ci/manifests/` fixtures | `.github/workflows/lint-test.yaml` | | helm-docs drift gate (`git diff` fail step) | hardened variant in CI | diff --git a/.agents/structure.md b/.agents/structure.md index fc2b612..76a7dfa 100644 --- a/.agents/structure.md +++ b/.agents/structure.md @@ -26,7 +26,7 @@ authup/helm │ ├── mysql-values.yaml │ ├── external-db-values.yaml # externalDatabase + existingSecret, against ci/manifests fixture │ ├── valkey-values.yaml # cache + 2 replicas + migration hook - │ ├── server-only-values.yaml # headless IdP (ui.enabled=false) + │ ├── server-only-values.yaml # headless IdP (adminConsole.enabled=false) │ └── manifests/postgres.yaml # fixtures pre-applied before ct install └── templates/ ├── _helpers.tpl # names, labels, images, tplvalues, affinity, securityContext @@ -35,7 +35,7 @@ authup/helm ├── _urls.tpl # publicUrl/apiUrl/origin derivation + post-render scheme asserts ├── _ingress.tpl # shared Ingress + HTTPRoute renderers (server and ui call them) ├── _server-env.tpl # configEnv map, secretEnv list, shared volumes (deployment + job) - ├── _ui-env.tpl # ui configEnv map + ├── _admin-console-env.tpl # ui configEnv map ├── validations.yaml # render-nothing fail-fast guards (cross-field rules) ├── secret.yaml # chart-managed auth secret (admin password, system client, KEK) ├── secret-db.yaml # external-db password secret (no generation fallback) @@ -47,7 +47,7 @@ authup/helm ├── server/ # server-core: deployment, service, ingress, httproute, │ # configmap-env, configmap-configuration, configmap-provisioning, │ # migration-job, hpa, pdb, networkpolicy, servicemonitor - ├── ui/ # client-console: deployment, service, ingress, httproute, + ├── ui/ # client-admin-console: deployment, service, ingress, httproute, │ # configmap-env, hpa, pdb, networkpolicy ├── postgresql/ # built-in instance: statefulset, service, secret ├── mysql/ # built-in instance: statefulset, service, secret @@ -57,14 +57,14 @@ authup/helm ## Two components, per-role template directories `server/` (server-core, the IdP: OAuth2/OIDC surface + SSR auth pages) and -`ui/` (client-console admin console) are separate template directories with ~85% +`ui/` (client-admin-console admin console) are separate template directories with ~85% similar deployment templates. This duplication is DELIBERATE: authentik built the DRY role-loop and reverted it ("takes DRY maybe a bit too far", their PR #163). Do not introduce a role loop. A future authup server/worker split becomes a third directory with the same skeleton. Both services run the SAME image (`authup/authup`) with different args -(`server/core start` vs `client/console start`). The image entrypoint force-exports +(`server/core start` vs `client/admin-console start`). The image entrypoint force-exports `PORT=3000` for both, so `containerPort` is pinned to 3000 everywhere and only Service ports are values. diff --git a/.agents/testing.md b/.agents/testing.md index 598e2ba..cbe742b 100644 --- a/.agents/testing.md +++ b/.agents/testing.md @@ -23,7 +23,7 @@ helm template test charts/authup # defaults (b helm template test charts/authup -f charts/authup/ci/valkey-values.yaml helm template test charts/authup --set server.ingress.enabled=true \ --set server.ingress.hostname=auth.example.com --set server.ingress.tls=true \ - --set ui.ingress.enabled=true --set ui.ingress.hostname=app.example.com --set ui.ingress.tls=true + --set adminConsole.ingress.enabled=true --set adminConsole.ingress.hostname=app.example.com --set adminConsole.ingress.tls=true ``` When verifying env wiring, grep the rendered ConfigMaps/Deployments for diff --git a/AGENTS.md b/AGENTS.md index da73691..016ba63 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ Helm charts for [Authup](https://authup.org), an authentication & authorization system. One application chart today: `charts/authup` deploys the two runtime services of the [authup monorepo](https://github.com/authup/authup) (server-core -IdP/API and the client-console admin UI) plus optional built-in PostgreSQL, MySQL +IdP/API and the client-admin-console admin UI) plus optional built-in PostgreSQL, MySQL and Valkey instances. `DESIGN.md` at the repo root is the authoritative design record: every major diff --git a/DESIGN.md b/DESIGN.md index 074780e..f9dfbe7 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -23,7 +23,7 @@ repo; every decision below cites its evidence. vendored locally (Apache-2.0-clean, bitnami-compatible key names). 2. **Two workloads, one chart, one image.** `authup/authup` is a single image whose entrypoint dispatches on args: `server/core start` (server-core, the IdP/API) and - `client/console start` (client-console, the Nuxt admin UI). The `authup` CLI supervisor is + `client/admin-console start` (client-admin-console, the Nuxt admin UI). The `authup` CLI supervisor is **not routable through the container entrypoint** and the monorepo docs pin "containers with one service each" as the production topology — so the chart ships two Deployments and never a combined pod. @@ -58,7 +58,7 @@ authup/helm │ │ ├── mysql-values.yaml # bundled mysql │ │ ├── external-db-values.yaml # externalDatabase + existingSecret fixture │ │ ├── redis-values.yaml # bundled valkey + replicas 2 -│ │ └── server-only-values.yaml # ui.enabled=false (headless deployment) +│ │ └── server-only-values.yaml # adminConsole.enabled=false (headless deployment) │ ├── ci/manifests/ # fixtures pre-applied before ct install │ └── templates/ │ ├── _helpers.tpl # names, labels, images (vendored bitnami-compatible) @@ -76,7 +76,7 @@ authup/helm │ ├── server/ # server-core: deployment, service, ingress, │ │ # httproute, configmap-env, migration-job, hpa, │ │ # pdb, networkpolicy, servicemonitor -│ ├── ui/ # client-console: deployment, service, ingress, +│ ├── ui/ # client-admin-console: deployment, service, ingress, │ │ # httproute, configmap-env, hpa, pdb, networkpolicy │ ├── postgresql/ # optional built-in dev instance (statefulset, │ │ # service, secret) — docker-official image @@ -100,15 +100,15 @@ charts (e.g. an `authup-remote` RBAC chart, authentik-style). ### 3.1 Workloads -| | `server` (server-core) | `ui` (client-console) | +| | `server` (server-core) | `ui` (client-admin-console) | |---|---|---| -| args | `["server/core", "start"]` | `["client/console", "start"]` | +| args | `["server/core", "start"]` | `["client/admin-console", "start"]` | | containerPort | 3000 (pinned) | 3000 (pinned) | | role | OAuth2/OIDC IdP origin + SSR auth pages | admin console, ordinary OAuth2 RP | | state | stateless w/ external DB+redis | fully stateless | | probes | httpGet `/` (status endpoint); generous startupProbe (boot = migrate + provision) | httpGet `/` | | scaling | replicas > 1 **requires redis** (hard template fail) | free | -| default | enabled | enabled (`ui.enabled: false` = headless IdP) | +| default | enabled | enabled (`adminConsole.enabled: false` = headless IdP) | Per-role template directories, ~85% duplication between the two deployment templates **accepted deliberately** — authentik tried the DRY role-loop and @@ -124,7 +124,7 @@ Vendored helpers (bitnami-compatible semantics, local implementation): - `authup.fullname` — release-scoped, honors `fullnameOverride` (bitnami key name, not Authelia's confusing nameOverride-acts-as-fullname variant), 63-char safe. -- `authup.server.fullname` / `authup.ui.fullname` — `-server` / `-ui`. +- `authup.server.fullname` / `authup.adminConsole.fullname` — `-server` / `-ui`. - `authup.labels.standard` / `authup.labels.matchLabels` — the five `app.kubernetes.io/*` labels; selectors carry ONLY name+instance+component (user `commonLabels` never leak into immutable selectors — the bitnami `pick` @@ -274,7 +274,7 @@ externalRedis: ### 3.7 Ingress and topology Two-host model as the default (server-core is the IdP origin serving the SSR auth -pages; client-console is an ordinary RP; **cookie-domain sharing between the two is +pages; client-admin-console is an ordinary RP; **cookie-domain sharing between the two is unsupported by authup** — the chart never sets `NUXT_PUBLIC_COOKIE_DOMAIN` and validates against foot-guns): @@ -296,7 +296,7 @@ publicUrl drift): - `PUBLIC_URL` ← `server.publicUrl` | derived `http(s)://` - `NUXT_PUBLIC_API_URL` ← the same value (browser-reachable, never the cluster Service DNS; the optional private `NUXT_API_URL` may point in-cluster for SSR) -- `NUXT_PUBLIC_PUBLIC_URL` ← `ui.publicUrl` | derived from `ui.ingress` +- `NUXT_PUBLIC_PUBLIC_URL` ← `adminConsole.publicUrl` | derived from `adminConsole.ingress` - `TRUSTED_ORIGINS` ← user list ∪ the UI origin (auto-appended unless disabled) - `TRUST_PROXY` defaults to `"1"` (one ingress hop), not authup's spoofable `true`-every-hop default. @@ -386,7 +386,7 @@ Cross-field rules the JSON schema cannot express, one render-nothing template: 3. `mfa.required` without `mfa.enabled`; `loginThrottle` without event log (mirrors authup's boot validations — fail at render, not at CrashLoopBackOff). 4. `auth.existingSecret` combined with inline passwords. -5. `ui.enabled` with neither ingress nor explicit `ui.publicUrl` when server +5. `adminConsole.enabled` with neither ingress nor explicit `adminConsole.publicUrl` when server ingress is on (dead-login trap), and any config that would point the UI cookie domain at the server host. 6. Tombstones for renamed values (grows over time). diff --git a/README.md b/README.md index 735dea9..899bb2a 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ on Kubernetes. ## Highlights - 🔐 **Complete deployment** - the server-core IdP/API (OAuth2 / OpenID Connect, - hosted login & consent pages) and the client-console admin UI, from one chart + hosted login & consent pages) and the client-admin-console admin UI, from one chart - 🗄️ **Hybrid database model** - built-in PostgreSQL **or** MySQL for a one-command start, or bring your own external database - ⚡ **Optional Valkey cache** - built-in instance or external Redis; required @@ -100,7 +100,7 @@ key). | Chart | Description | |---|---| -| [authup](./charts/authup) | server-core (IdP/API) + client-console (admin UI), optional built-in PostgreSQL / MySQL / Valkey | +| [authup](./charts/authup) | server-core (IdP/API) + client-admin-console (admin UI), optional built-in PostgreSQL / MySQL / Valkey | ## Documentation diff --git a/charts/authup/BREAKING.md b/charts/authup/BREAKING.md index b8b9952..0f8fe35 100644 --- a/charts/authup/BREAKING.md +++ b/charts/authup/BREAKING.md @@ -3,4 +3,21 @@ This chart uses `0.major.minor` versioning while below 1.0.0: breaking changes land on the middle digit. Every entry lists the value migrations required. -_No breaking changes yet._ +## 0.2.0 (unreleased) + +Follows the upstream rename of the admin UI app (authup/authup#3370) and its +dedicated OAuth2 client (authup/authup#3371). + +- Values section `ui.*` -> `adminConsole.*`. Every key moves unchanged, e.g. + `ui.enabled` -> `adminConsole.enabled`, `ui.ingress.hostname` -> + `adminConsole.ingress.hostname`. +- `server.trustedOriginsAppendUI` -> `server.trustedOriginsAppendAdminConsole`. +- Rendered resource names change suffix `-ui` -> `-admin-console` + (Deployment, Service, Ingress, ...). Helm re-creates them on upgrade; + expect a brief admin-UI rollout and update anything referencing the old + Service name directly. +- The admin UI container now starts with `client/admin-console start` and + logs in against the per-realm `admin-console` OAuth2 client. Requires an + authup image containing authup/authup#3370 + #3371; older images only know + `client/web` and would crash-loop. Ship this chart version together with + the `appVersion` bump to that release. diff --git a/charts/authup/Chart.yaml b/charts/authup/Chart.yaml index 88c22f2..ce034ba 100644 --- a/charts/authup/Chart.yaml +++ b/charts/authup/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: authup -description: Authup is an authentication & authorization system. This chart deploys the server-core IdP/API service and the client-console admin UI, with optional built-in PostgreSQL, MySQL and Valkey instances. +description: Authup is an authentication & authorization system. This chart deploys the server-core IdP/API service and the client-admin-console admin UI, with optional built-in PostgreSQL, MySQL and Valkey instances. type: application version: 0.1.0 appVersion: "1.0.0-beta.58" diff --git a/charts/authup/README.md b/charts/authup/README.md index 6bae492..570f2b9 100644 --- a/charts/authup/README.md +++ b/charts/authup/README.md @@ -8,13 +8,13 @@ ![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.0.0-beta.58](https://img.shields.io/badge/AppVersion-1.0.0--beta.58-informational?style=flat-square) -Authup is an authentication & authorization system. This chart deploys the server-core IdP/API service and the client-console admin UI, with optional built-in PostgreSQL, MySQL and Valkey instances. It deploys: +Authup is an authentication & authorization system. This chart deploys the server-core IdP/API service and the client-admin-console admin UI, with optional built-in PostgreSQL, MySQL and Valkey instances. It deploys: - **server-core** — the Authup IdP/API service: the OAuth2/OIDC protocol surface plus the server-rendered auth pages (login, consent, registration, password recovery). This is the identity origin. -- **client-console** — the Nuxt-based admin UI, an ordinary OAuth2 relying party - (optional; disable with `ui.enabled=false` for a headless IdP). +- **client-admin-console** — the Nuxt-based admin UI, an ordinary OAuth2 relying party + (optional; disable with `adminConsole.enabled=false` for a headless IdP). - optionally, single-instance **PostgreSQL**, **MySQL** or **Valkey** built-in instances on docker-official images — a convenience for dev and small deployments, not the production database story. @@ -111,6 +111,102 @@ Kubernetes: `>=1.25.0-0` | Key | Type | Default | Description | |-----|------|---------|-------------| +| adminConsole.affinity | object | `{}` | Affinity (overrides the anti-affinity preset when set) | +| adminConsole.apiUrl | string | `""` | Browser-facing server-core URL (NUXT_PUBLIC_API_URL). "" = the server public URL. Must be reachable from the user's browser, never a cluster-internal DNS name | +| adminConsole.args | list | `[]` | Override the container args | +| adminConsole.autoscaling.hpa.enabled | bool | `false` | Enable HPA for the UI | +| adminConsole.autoscaling.hpa.maxReplicas | int | `5` | Maximum replicas | +| adminConsole.autoscaling.hpa.minReplicas | int | `2` | Minimum replicas | +| adminConsole.autoscaling.hpa.targetCPU | int | `75` | Target CPU utilization percentage | +| adminConsole.autoscaling.hpa.targetMemory | string | `""` | Target memory utilization percentage | +| adminConsole.command | list | `[]` | Override the container command | +| adminConsole.config | object | `{}` | Extra environment variables rendered literally into the env ConfigMap | +| adminConsole.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"enabled":true,"readOnlyRootFilesystem":false,"runAsNonRoot":false,"runAsUser":0,"seccompProfile":{"type":"RuntimeDefault"}}` | Container security context (same root-image caveat as the server) | +| adminConsole.customLivenessProbe | object | `{}` | Custom liveness probe | +| adminConsole.customReadinessProbe | object | `{}` | Custom readiness probe | +| adminConsole.customStartupProbe | object | `{}` | Custom startup probe | +| adminConsole.disableRestartOnChanges | bool | `false` | Disable the checksum annotations that roll pods on config changes | +| adminConsole.enabled | bool | `true` | Deploy the client-admin-console admin UI (false = headless IdP) | +| adminConsole.extraEnvVars | list | `[]` | Extra environment variables for the UI container | +| adminConsole.extraEnvVarsCM | string | `""` | Extra ConfigMap with environment variables (tpl-rendered name) | +| adminConsole.extraEnvVarsSecret | string | `""` | Extra Secret with environment variables (tpl-rendered name) | +| adminConsole.extraVolumeMounts | list | `[]` | Extra volume mounts (tpl-rendered) | +| adminConsole.extraVolumes | list | `[]` | Extra volumes (tpl-rendered) | +| adminConsole.hostAliases | list | `[]` | Pod host aliases | +| adminConsole.ingress.annotations | object | `{}` | Ingress annotations (tpl-rendered) | +| adminConsole.ingress.certManager | bool | `false` | Request a cert-manager certificate (adds kubernetes.io/tls-acme) | +| 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.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 | +| adminConsole.ingress.path | string | `"/"` | Ingress path | +| adminConsole.ingress.pathType | string | `"Prefix"` | Ingress path type | +| adminConsole.ingress.tls | bool | `false` | Enable TLS for the hostname | +| adminConsole.initContainers | list | `[]` | Init containers (tpl-rendered) | +| adminConsole.internalApiUrl | string | `""` | Server-side (SSR) API URL override (NUXT_API_URL), e.g. the in-cluster service URL to keep SSR traffic off the ingress | +| adminConsole.lifecycleHooks | object | `{}` | Container lifecycle hooks | +| adminConsole.livenessProbe.enabled | bool | `true` | Enable the liveness probe | +| adminConsole.livenessProbe.failureThreshold | int | `3` | | +| adminConsole.livenessProbe.initialDelaySeconds | int | `0` | | +| adminConsole.livenessProbe.periodSeconds | int | `30` | | +| adminConsole.livenessProbe.successThreshold | int | `1` | | +| adminConsole.livenessProbe.timeoutSeconds | int | `5` | | +| adminConsole.networkPolicy.allowExternal | bool | `true` | Allow ingress from anywhere | +| adminConsole.networkPolicy.allowExternalEgress | bool | `true` | Allow all egress | +| adminConsole.networkPolicy.enabled | bool | `false` | Create a NetworkPolicy for the UI | +| adminConsole.networkPolicy.extraEgress | list | `[]` | Extra egress rules | +| adminConsole.networkPolicy.extraIngress | list | `[]` | Extra ingress rules | +| adminConsole.networkPolicy.ingressNSMatchLabels | object | `{}` | Namespace labels allowed to connect when allowExternal is false | +| adminConsole.networkPolicy.ingressPodMatchLabels | object | `{}` | Pod labels allowed to connect when allowExternal is false | +| adminConsole.nodeSelector | object | `{}` | Node selector | +| adminConsole.pdb.create | bool | `false` | Create a PodDisruptionBudget for the UI | +| adminConsole.pdb.maxUnavailable | string | `""` | Maximum unavailable pods (defaults to 1 when both are empty) | +| adminConsole.pdb.minAvailable | string | `""` | Minimum available pods | +| adminConsole.podAnnotations | object | `{}` | Pod annotations (tpl-rendered) | +| adminConsole.podAntiAffinityPreset | string | `"soft"` | Pod anti-affinity preset: soft, hard or "" | +| adminConsole.podLabels | object | `{}` | Pod labels (tpl-rendered) | +| adminConsole.podSecurityContext | object | `{"enabled":true,"fsGroup":1000}` | Pod security context | +| adminConsole.priorityClassName | string | `""` | Priority class name | +| adminConsole.publicUrl | string | `""` | Public URL of the UI (NUXT_PUBLIC_PUBLIC_URL). "" = derived from adminConsole.ingress | +| adminConsole.readinessProbe.enabled | bool | `true` | Enable the readiness probe | +| adminConsole.readinessProbe.failureThreshold | int | `3` | | +| adminConsole.readinessProbe.initialDelaySeconds | int | `0` | | +| adminConsole.readinessProbe.periodSeconds | int | `10` | | +| adminConsole.readinessProbe.successThreshold | int | `1` | | +| adminConsole.readinessProbe.timeoutSeconds | int | `5` | | +| adminConsole.replicaCount | int | `1` | Number of UI replicas (fully stateless, scale freely) | +| adminConsole.resources | object | `{"limits":{"memory":"512Mi"},"requests":{"cpu":"100m","memory":"256Mi"}}` | UI container resources | +| 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.parentRefs | list | `[]` | Gateway parentRefs | +| adminConsole.schedulerName | string | `""` | Scheduler name | +| adminConsole.service.annotations | object | `{}` | Service annotations (tpl-rendered) | +| adminConsole.service.clusterIP | string | `""` | Static cluster IP | +| adminConsole.service.externalTrafficPolicy | string | `"Cluster"` | External traffic policy | +| adminConsole.service.extraPorts | list | `[]` | Extra service ports | +| adminConsole.service.loadBalancerIP | string | `""` | LoadBalancer IP | +| adminConsole.service.loadBalancerSourceRanges | list | `[]` | LoadBalancer source ranges | +| adminConsole.service.nodePorts.http | string | `""` | Node port ("" = auto-assign) | +| adminConsole.service.ports.http | int | `3000` | Service HTTP port (the container port is fixed at 3000) | +| adminConsole.service.sessionAffinity | string | `"None"` | Session affinity | +| adminConsole.service.sessionAffinityConfig | object | `{}` | Session affinity config | +| adminConsole.service.type | string | `"ClusterIP"` | Service type | +| adminConsole.sidecars | list | `[]` | Sidecar containers (tpl-rendered) | +| adminConsole.startupProbe.enabled | bool | `true` | Enable the startup probe | +| adminConsole.startupProbe.failureThreshold | int | `24` | | +| adminConsole.startupProbe.initialDelaySeconds | int | `5` | | +| adminConsole.startupProbe.periodSeconds | int | `5` | | +| adminConsole.startupProbe.successThreshold | int | `1` | | +| adminConsole.startupProbe.timeoutSeconds | int | `5` | | +| adminConsole.terminationGracePeriodSeconds | int | `30` | Pod termination grace period | +| adminConsole.tolerations | list | `[]` | Tolerations | +| adminConsole.topologySpreadConstraints | list | `[]` | Topology spread constraints | +| adminConsole.updateStrategy | object | `{"type":"RollingUpdate"}` | Deployment update strategy | | auth.adminPassword | string | `""` | Initial admin user password ("" = generate once, keep across upgrades). Changing it after the first install only takes effect with adminPasswordReset=true for one upgrade cycle | | auth.adminPasswordReset | bool | `false` | Re-assert the admin password on every boot (USER_ADMIN_PASSWORD_RESET) | | auth.existingSecret | string | `""` | Existing secret holding the keys below instead of the chart-managed secret (tpl-rendered) | @@ -313,7 +409,7 @@ Kubernetes: `>=1.25.0-0` | server.topologySpreadConstraints | list | `[]` | Topology spread constraints (a missing labelSelector is filled with the pod's selector labels) | | server.trustProxy | string | `"1"` | TRUST_PROXY setting. The chart defaults to one trusted hop (the ingress), not authup's spoofable trust-everything default | | server.trustedOrigins | list | `[]` | Additional trusted first-party app origins (TRUSTED_ORIGINS). Each listed origin can obtain full-permission tokens via the per-realm web client. List or comma-separated string; tpl-rendered. | -| server.trustedOriginsAppendUI | bool | `true` | Automatically append the client-console UI origin to TRUSTED_ORIGINS (removes the most common dead-login misconfiguration) | +| server.trustedOriginsAppendAdminConsole | bool | `true` | Automatically append the client-admin-console UI origin to TRUSTED_ORIGINS (removes the most common dead-login misconfiguration) | | server.updateStrategy | object | `{"type":"RollingUpdate"}` | Deployment update strategy | | serviceAccount.annotations | object | `{}` | ServiceAccount annotations (tpl-rendered) | | serviceAccount.automountServiceAccountToken | bool | `false` | Automount the service account token | @@ -322,102 +418,6 @@ Kubernetes: `>=1.25.0-0` | smtp.connectionString | string | `""` | SMTP connection string (smtp(s)://user:pass@host:port); stored in a chart-managed secret | | smtp.existingSecret | string | `""` | Existing secret holding the SMTP connection string (tpl-rendered) | | smtp.existingSecretKey | string | `"smtp-connection-string"` | Key inside smtp.existingSecret holding the connection string | -| ui.affinity | object | `{}` | Affinity (overrides the anti-affinity preset when set) | -| ui.apiUrl | string | `""` | Browser-facing server-core URL (NUXT_PUBLIC_API_URL). "" = the server public URL. Must be reachable from the user's browser, never a cluster-internal DNS name | -| ui.args | list | `[]` | Override the container args | -| ui.autoscaling.hpa.enabled | bool | `false` | Enable HPA for the UI | -| ui.autoscaling.hpa.maxReplicas | int | `5` | Maximum replicas | -| ui.autoscaling.hpa.minReplicas | int | `2` | Minimum replicas | -| ui.autoscaling.hpa.targetCPU | int | `75` | Target CPU utilization percentage | -| ui.autoscaling.hpa.targetMemory | string | `""` | Target memory utilization percentage | -| ui.command | list | `[]` | Override the container command | -| ui.config | object | `{}` | Extra environment variables rendered literally into the env ConfigMap | -| ui.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"enabled":true,"readOnlyRootFilesystem":false,"runAsNonRoot":false,"runAsUser":0,"seccompProfile":{"type":"RuntimeDefault"}}` | Container security context (same root-image caveat as the server) | -| ui.customLivenessProbe | object | `{}` | Custom liveness probe | -| ui.customReadinessProbe | object | `{}` | Custom readiness probe | -| ui.customStartupProbe | object | `{}` | Custom startup probe | -| ui.disableRestartOnChanges | bool | `false` | Disable the checksum annotations that roll pods on config changes | -| ui.enabled | bool | `true` | Deploy the client-console admin UI (false = headless IdP) | -| ui.extraEnvVars | list | `[]` | Extra environment variables for the UI container | -| ui.extraEnvVarsCM | string | `""` | Extra ConfigMap with environment variables (tpl-rendered name) | -| ui.extraEnvVarsSecret | string | `""` | Extra Secret with environment variables (tpl-rendered name) | -| ui.extraVolumeMounts | list | `[]` | Extra volume mounts (tpl-rendered) | -| ui.extraVolumes | list | `[]` | Extra volumes (tpl-rendered) | -| ui.hostAliases | list | `[]` | Pod host aliases | -| ui.ingress.annotations | object | `{}` | Ingress annotations (tpl-rendered) | -| ui.ingress.certManager | bool | `false` | Request a cert-manager certificate (adds kubernetes.io/tls-acme) | -| ui.ingress.enabled | bool | `false` | Enable ingress for the UI | -| ui.ingress.extraHosts | list | `[]` | Extra hosts | -| ui.ingress.extraPaths | list | `[]` | Extra paths for the primary host | -| ui.ingress.extraRules | list | `[]` | Full custom rules (tpl-rendered; overrides the generated rule) | -| ui.ingress.extraTls | list | `[]` | Extra TLS entries | -| ui.ingress.hostname | string | `""` | Ingress hostname (tpl-rendered); also drives the derived UI public URL | -| ui.ingress.ingressClassName | string | `""` | Ingress class name | -| ui.ingress.path | string | `"/"` | Ingress path | -| ui.ingress.pathType | string | `"Prefix"` | Ingress path type | -| ui.ingress.tls | bool | `false` | Enable TLS for the hostname | -| ui.initContainers | list | `[]` | Init containers (tpl-rendered) | -| ui.internalApiUrl | string | `""` | Server-side (SSR) API URL override (NUXT_API_URL), e.g. the in-cluster service URL to keep SSR traffic off the ingress | -| ui.lifecycleHooks | object | `{}` | Container lifecycle hooks | -| ui.livenessProbe.enabled | bool | `true` | Enable the liveness probe | -| ui.livenessProbe.failureThreshold | int | `3` | | -| ui.livenessProbe.initialDelaySeconds | int | `0` | | -| ui.livenessProbe.periodSeconds | int | `30` | | -| ui.livenessProbe.successThreshold | int | `1` | | -| ui.livenessProbe.timeoutSeconds | int | `5` | | -| ui.networkPolicy.allowExternal | bool | `true` | Allow ingress from anywhere | -| ui.networkPolicy.allowExternalEgress | bool | `true` | Allow all egress | -| ui.networkPolicy.enabled | bool | `false` | Create a NetworkPolicy for the UI | -| ui.networkPolicy.extraEgress | list | `[]` | Extra egress rules | -| ui.networkPolicy.extraIngress | list | `[]` | Extra ingress rules | -| ui.networkPolicy.ingressNSMatchLabels | object | `{}` | Namespace labels allowed to connect when allowExternal is false | -| ui.networkPolicy.ingressPodMatchLabels | object | `{}` | Pod labels allowed to connect when allowExternal is false | -| ui.nodeSelector | object | `{}` | Node selector | -| ui.pdb.create | bool | `false` | Create a PodDisruptionBudget for the UI | -| ui.pdb.maxUnavailable | string | `""` | Maximum unavailable pods (defaults to 1 when both are empty) | -| ui.pdb.minAvailable | string | `""` | Minimum available pods | -| ui.podAnnotations | object | `{}` | Pod annotations (tpl-rendered) | -| ui.podAntiAffinityPreset | string | `"soft"` | Pod anti-affinity preset: soft, hard or "" | -| ui.podLabels | object | `{}` | Pod labels (tpl-rendered) | -| ui.podSecurityContext | object | `{"enabled":true,"fsGroup":1000}` | Pod security context | -| ui.priorityClassName | string | `""` | Priority class name | -| ui.publicUrl | string | `""` | Public URL of the UI (NUXT_PUBLIC_PUBLIC_URL). "" = derived from ui.ingress | -| ui.readinessProbe.enabled | bool | `true` | Enable the readiness probe | -| ui.readinessProbe.failureThreshold | int | `3` | | -| ui.readinessProbe.initialDelaySeconds | int | `0` | | -| ui.readinessProbe.periodSeconds | int | `10` | | -| ui.readinessProbe.successThreshold | int | `1` | | -| ui.readinessProbe.timeoutSeconds | int | `5` | | -| ui.replicaCount | int | `1` | Number of UI replicas (fully stateless, scale freely) | -| ui.resources | object | `{"limits":{"memory":"512Mi"},"requests":{"cpu":"100m","memory":"256Mi"}}` | UI container resources | -| ui.revisionHistoryLimit | int | `3` | Deployment revision history limit | -| ui.route.annotations | object | `{}` | HTTPRoute annotations | -| ui.route.enabled | bool | `false` | Create a Gateway API HTTPRoute for the UI | -| ui.route.hostnames | list | `[]` | Route hostnames ([] = derived from ui.publicUrl / ingress hostname) | -| ui.route.parentRefs | list | `[]` | Gateway parentRefs | -| ui.schedulerName | string | `""` | Scheduler name | -| ui.service.annotations | object | `{}` | Service annotations (tpl-rendered) | -| ui.service.clusterIP | string | `""` | Static cluster IP | -| ui.service.externalTrafficPolicy | string | `"Cluster"` | External traffic policy | -| ui.service.extraPorts | list | `[]` | Extra service ports | -| ui.service.loadBalancerIP | string | `""` | LoadBalancer IP | -| ui.service.loadBalancerSourceRanges | list | `[]` | LoadBalancer source ranges | -| ui.service.nodePorts.http | string | `""` | Node port ("" = auto-assign) | -| ui.service.ports.http | int | `3000` | Service HTTP port (the container port is fixed at 3000) | -| ui.service.sessionAffinity | string | `"None"` | Session affinity | -| ui.service.sessionAffinityConfig | object | `{}` | Session affinity config | -| ui.service.type | string | `"ClusterIP"` | Service type | -| ui.sidecars | list | `[]` | Sidecar containers (tpl-rendered) | -| ui.startupProbe.enabled | bool | `true` | Enable the startup probe | -| ui.startupProbe.failureThreshold | int | `24` | | -| ui.startupProbe.initialDelaySeconds | int | `5` | | -| ui.startupProbe.periodSeconds | int | `5` | | -| ui.startupProbe.successThreshold | int | `1` | | -| ui.startupProbe.timeoutSeconds | int | `5` | | -| ui.terminationGracePeriodSeconds | int | `30` | Pod termination grace period | -| ui.tolerations | list | `[]` | Tolerations | -| ui.topologySpreadConstraints | list | `[]` | Topology spread constraints | -| ui.updateStrategy | object | `{"type":"RollingUpdate"}` | Deployment update strategy | | useHelmHooks | bool | `true` | Render Job hook annotations (set false for ArgoCD / Flux) | | valkey.affinity | object | `{}` | Valkey affinity | | valkey.auth.password | string | `""` | Valkey password ("" = generate once, keep across upgrades) | diff --git a/charts/authup/README.md.gotmpl b/charts/authup/README.md.gotmpl index 36eeb63..ee9178a 100644 --- a/charts/authup/README.md.gotmpl +++ b/charts/authup/README.md.gotmpl @@ -15,8 +15,8 @@ - **server-core** — the Authup IdP/API service: the OAuth2/OIDC protocol surface plus the server-rendered auth pages (login, consent, registration, password recovery). This is the identity origin. -- **client-console** — the Nuxt-based admin UI, an ordinary OAuth2 relying party - (optional; disable with `ui.enabled=false` for a headless IdP). +- **client-admin-console** — the Nuxt-based admin UI, an ordinary OAuth2 relying party + (optional; disable with `adminConsole.enabled=false` for a headless IdP). - optionally, single-instance **PostgreSQL**, **MySQL** or **Valkey** built-in instances on docker-official images — a convenience for dev and small deployments, not the production database story. diff --git a/charts/authup/ci/default-values.yaml b/charts/authup/ci/default-values.yaml index 5311aff..8a84938 100644 --- a/charts/authup/ci/default-values.yaml +++ b/charts/authup/ci/default-values.yaml @@ -7,7 +7,7 @@ server: requests: cpu: 50m memory: 256Mi -ui: +adminConsole: resources: requests: cpu: 25m diff --git a/charts/authup/ci/external-db-values.yaml b/charts/authup/ci/external-db-values.yaml index 1a95b50..1a9b7da 100644 --- a/charts/authup/ci/external-db-values.yaml +++ b/charts/authup/ci/external-db-values.yaml @@ -16,5 +16,5 @@ server: requests: cpu: 50m memory: 256Mi -ui: +adminConsole: enabled: false diff --git a/charts/authup/ci/mysql-values.yaml b/charts/authup/ci/mysql-values.yaml index d6c614f..4bde7a3 100644 --- a/charts/authup/ci/mysql-values.yaml +++ b/charts/authup/ci/mysql-values.yaml @@ -10,7 +10,7 @@ server: requests: cpu: 50m memory: 256Mi -ui: +adminConsole: resources: requests: cpu: 25m diff --git a/charts/authup/ci/server-only-values.yaml b/charts/authup/ci/server-only-values.yaml index 512d0f5..60eeaba 100644 --- a/charts/authup/ci/server-only-values.yaml +++ b/charts/authup/ci/server-only-values.yaml @@ -2,7 +2,7 @@ postgresql: persistence: enabled: false -ui: +adminConsole: enabled: false server: resources: diff --git a/charts/authup/ci/valkey-values.yaml b/charts/authup/ci/valkey-values.yaml index eea0654..ccfa0a0 100644 --- a/charts/authup/ci/valkey-values.yaml +++ b/charts/authup/ci/valkey-values.yaml @@ -12,7 +12,7 @@ server: requests: cpu: 50m memory: 256Mi -ui: +adminConsole: resources: requests: cpu: 25m diff --git a/charts/authup/templates/NOTES.txt b/charts/authup/templates/NOTES.txt index 9d62a77..6e4a8e3 100644 --- a/charts/authup/templates/NOTES.txt +++ b/charts/authup/templates/NOTES.txt @@ -1,7 +1,7 @@ ** Authup {{ .Chart.AppVersion }} has been installed as release {{ .Release.Name }}. ** {{- $serverUrl := include "authup.server.publicUrl" . }} -{{- $uiUrl := include "authup.ui.publicUrl" . }} +{{- $uiUrl := include "authup.adminConsole.publicUrl" . }} {{- if .Values.server.enabled }} @@ -25,14 +25,14 @@ server-core (IdP / API): {{- end }} {{- end }} -{{- if .Values.ui.enabled }} +{{- if .Values.adminConsole.enabled }} -client-console (admin UI): +client-admin-console (admin UI): {{- if $uiUrl }} URL: {{ $uiUrl }} {{- else }} - In-cluster URL: http://{{ include "authup.ui.fullname" . }}.{{ include "authup.namespace" . }}.svc:{{ .Values.ui.service.ports.http }} - (port-forward: kubectl -n {{ include "authup.namespace" . }} port-forward svc/{{ include "authup.ui.fullname" . }} 3000:{{ .Values.ui.service.ports.http }}) + In-cluster URL: http://{{ include "authup.adminConsole.fullname" . }}.{{ include "authup.namespace" . }}.svc:{{ .Values.adminConsole.service.ports.http }} + (port-forward: kubectl -n {{ include "authup.namespace" . }} port-forward svc/{{ include "authup.adminConsole.fullname" . }} 3000:{{ .Values.adminConsole.service.ports.http }}) {{- end }} {{- end }} @@ -63,11 +63,11 @@ value. In GitOps setups, set explicit auth values or auth.existingSecret. {{- end }} {{- end }} -{{- if and .Values.ui.enabled (not $uiUrl) $serverUrl }} +{{- if and .Values.adminConsole.enabled (not $uiUrl) $serverUrl }} -WARNING: the UI has no resolvable public URL (ui.publicUrl / ui.ingress). Its +WARNING: the UI has no resolvable public URL (adminConsole.publicUrl / adminConsole.ingress). Its origin cannot be appended to TRUSTED_ORIGINS, and logins from the UI will fail -after the OAuth2 redirect. Set ui.ingress.hostname or ui.publicUrl. +after the OAuth2 redirect. Set adminConsole.ingress.hostname or adminConsole.publicUrl. {{- end }} {{- if and (gt (int .Values.server.replicaCount) 1) .Values.server.migration.enabled }} diff --git a/charts/authup/templates/_ui-env.tpl b/charts/authup/templates/_admin-console-env.tpl similarity index 53% rename from charts/authup/templates/_ui-env.tpl rename to charts/authup/templates/_admin-console-env.tpl index 01eda0c..c5d101d 100644 --- a/charts/authup/templates/_ui-env.tpl +++ b/charts/authup/templates/_admin-console-env.tpl @@ -1,23 +1,23 @@ {{/* -client-console environment as a YAML map. The published UI bundle only honors Nuxt +client-admin-console environment as a YAML map. The published UI bundle only honors Nuxt runtime-config names (NUXT_*); the API URL must be the BROWSER-reachable server-core URL, never a cluster-internal service name. The chart deliberately never sets NUXT_PUBLIC_COOKIE_DOMAIN: sharing a cookie -domain between client-console and the hosted auth pages is unsupported by authup. +domain between client-admin-console and the hosted auth pages is unsupported by authup. */}} -{{- define "authup.ui.configEnv" -}} -{{- $apiUrl := include "authup.ui.apiUrl" . }} +{{- define "authup.adminConsole.configEnv" -}} +{{- $apiUrl := include "authup.adminConsole.apiUrl" . }} {{- if $apiUrl }} NUXT_PUBLIC_API_URL: {{ $apiUrl | quote }} {{- end }} -{{- $publicUrl := include "authup.ui.publicUrl" . }} +{{- $publicUrl := include "authup.adminConsole.publicUrl" . }} {{- if $publicUrl }} NUXT_PUBLIC_PUBLIC_URL: {{ $publicUrl | quote }} {{- end }} -{{- if .Values.ui.internalApiUrl }} -NUXT_API_URL: {{ include "authup.tplvalues.render" (dict "value" .Values.ui.internalApiUrl "context" $) | quote }} +{{- if .Values.adminConsole.internalApiUrl }} +NUXT_API_URL: {{ include "authup.tplvalues.render" (dict "value" .Values.adminConsole.internalApiUrl "context" $) | quote }} {{- end }} -{{- range $key, $value := .Values.ui.config }} +{{- range $key, $value := .Values.adminConsole.config }} {{ $key }}: {{ include "authup.tplvalues.render" (dict "value" ($value | toString) "context" $) | quote }} {{- end }} {{- end -}} diff --git a/charts/authup/templates/_helpers.tpl b/charts/authup/templates/_helpers.tpl index 154ce04..9b362b4 100644 --- a/charts/authup/templates/_helpers.tpl +++ b/charts/authup/templates/_helpers.tpl @@ -30,8 +30,8 @@ component onto one identical name. {{- printf "%s-server" (include "authup.fullname" . | trunc 52 | trimSuffix "-") -}} {{- end -}} -{{- define "authup.ui.fullname" -}} -{{- printf "%s-ui" (include "authup.fullname" . | trunc 52 | trimSuffix "-") -}} +{{- define "authup.adminConsole.fullname" -}} +{{- printf "%s-admin-console" (include "authup.fullname" . | trunc 52 | trimSuffix "-") -}} {{- end -}} {{- define "authup.postgresql.fullname" -}} diff --git a/charts/authup/templates/_urls.tpl b/charts/authup/templates/_urls.tpl index f2b3d39..633ad92 100644 --- a/charts/authup/templates/_urls.tpl +++ b/charts/authup/templates/_urls.tpl @@ -1,5 +1,5 @@ {{/* -URL derivation. server.publicUrl / ui.publicUrl always win; otherwise the URL is +URL derivation. server.publicUrl / adminConsole.publicUrl always win; otherwise the URL is derived from the component's ingress (scheme from tls/certManager, host, path). Returns "" when nothing can be derived. */}} @@ -33,20 +33,20 @@ scheme-less result can never reach an env var or origin derivation. {{- end -}} {{- end -}} -{{- define "authup.ui.publicUrl" -}} -{{- if .Values.ui.publicUrl -}} -{{- include "authup.assertUrlScheme" (dict "key" "ui.publicUrl" "url" (include "authup.tplvalues.render" (dict "value" .Values.ui.publicUrl "context" $) | trimSuffix "/")) -}} +{{- define "authup.adminConsole.publicUrl" -}} +{{- if .Values.adminConsole.publicUrl -}} +{{- include "authup.assertUrlScheme" (dict "key" "adminConsole.publicUrl" "url" (include "authup.tplvalues.render" (dict "value" .Values.adminConsole.publicUrl "context" $) | trimSuffix "/")) -}} {{- else -}} -{{- include "authup.ingress.derivedUrl" (dict "ingress" .Values.ui.ingress "context" $) -}} +{{- include "authup.ingress.derivedUrl" (dict "ingress" .Values.adminConsole.ingress "context" $) -}} {{- end -}} {{- end -}} {{/* Browser-facing server-core URL for the UI (NUXT_PUBLIC_API_URL). */}} -{{- define "authup.ui.apiUrl" -}} -{{- if .Values.ui.apiUrl -}} -{{- include "authup.assertUrlScheme" (dict "key" "ui.apiUrl" "url" (include "authup.tplvalues.render" (dict "value" .Values.ui.apiUrl "context" $) | trimSuffix "/")) -}} +{{- define "authup.adminConsole.apiUrl" -}} +{{- if .Values.adminConsole.apiUrl -}} +{{- include "authup.assertUrlScheme" (dict "key" "adminConsole.apiUrl" "url" (include "authup.tplvalues.render" (dict "value" .Values.adminConsole.apiUrl "context" $) | trimSuffix "/")) -}} {{- else -}} {{- include "authup.server.publicUrl" . -}} {{- end -}} @@ -82,8 +82,8 @@ disabled or already covered by the server public URL's origin. {{- $origins = append $origins (trim (include "authup.tplvalues.render" (dict "value" . "context" $))) -}} {{- end -}} {{- end -}} -{{- if and .Values.ui.enabled .Values.server.trustedOriginsAppendUI -}} -{{- $uiOrigin := include "authup.urlOrigin" (include "authup.ui.publicUrl" .) -}} +{{- if and .Values.adminConsole.enabled .Values.server.trustedOriginsAppendAdminConsole -}} +{{- $uiOrigin := include "authup.urlOrigin" (include "authup.adminConsole.publicUrl" .) -}} {{- $serverOrigin := include "authup.urlOrigin" (include "authup.server.publicUrl" .) -}} {{- if and $uiOrigin (ne $uiOrigin $serverOrigin) -}} {{- $origins = append $origins $uiOrigin -}} diff --git a/charts/authup/templates/ui/configmap-env.yaml b/charts/authup/templates/admin-console/configmap-env.yaml similarity index 54% rename from charts/authup/templates/ui/configmap-env.yaml rename to charts/authup/templates/admin-console/configmap-env.yaml index a479327..1799903 100644 --- a/charts/authup/templates/ui/configmap-env.yaml +++ b/charts/authup/templates/admin-console/configmap-env.yaml @@ -1,10 +1,10 @@ -{{- if .Values.ui.enabled }} +{{- if .Values.adminConsole.enabled }} apiVersion: v1 kind: ConfigMap metadata: - name: {{ printf "%s-env" (include "authup.ui.fullname" .) }} + name: {{ printf "%s-env" (include "authup.adminConsole.fullname" .) }} namespace: {{ include "authup.namespace" . | quote }} - labels: {{- include "authup.labels" (dict "context" $ "component" "ui") | nindent 4 }} + labels: {{- include "authup.labels" (dict "context" $ "component" "admin-console") | nindent 4 }} annotations: {{- include "authup.annotations" (dict "context" $) | nindent 4 }} -data: {{- include "authup.ui.configEnv" . | nindent 2 }} +data: {{- include "authup.adminConsole.configEnv" . | nindent 2 }} {{- end }} diff --git a/charts/authup/templates/admin-console/deployment.yaml b/charts/authup/templates/admin-console/deployment.yaml new file mode 100644 index 0000000..b1cd407 --- /dev/null +++ b/charts/authup/templates/admin-console/deployment.yaml @@ -0,0 +1,159 @@ +{{- if .Values.adminConsole.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "authup.adminConsole.fullname" . }} + namespace: {{ include "authup.namespace" . | quote }} + labels: {{- include "authup.labels" (dict "context" $ "component" "admin-console") | nindent 4 }} + annotations: {{- include "authup.annotations" (dict "context" $) | nindent 4 }} +spec: + {{- if not .Values.adminConsole.autoscaling.hpa.enabled }} + replicas: {{ .Values.adminConsole.replicaCount }} + {{- end }} + revisionHistoryLimit: {{ .Values.adminConsole.revisionHistoryLimit }} + {{- if .Values.adminConsole.updateStrategy }} + strategy: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.updateStrategy "context" $) | nindent 4 }} + {{- end }} + selector: + matchLabels: {{- include "authup.matchLabels" (dict "context" $ "component" "admin-console") | nindent 6 }} + template: + metadata: + labels: {{- include "authup.labels" (dict "context" $ "component" "admin-console") | nindent 8 }} + {{- if .Values.adminConsole.podLabels }} + {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.podLabels "context" $) | nindent 8 }} + {{- end }} + annotations: + {{- if not .Values.adminConsole.disableRestartOnChanges }} + checksum/env: {{ include "authup.adminConsole.configEnv" . | sha256sum }} + {{- end }} + {{- if .Values.adminConsole.podAnnotations }} + {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.podAnnotations "context" $) | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "authup.serviceAccountName" . }} + automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} + {{- include "authup.imagePullSecrets" . | nindent 6 }} + {{- if .Values.adminConsole.podSecurityContext.enabled }} + securityContext: {{- include "authup.securityContext" .Values.adminConsole.podSecurityContext | nindent 8 }} + {{- end }} + {{- if .Values.adminConsole.priorityClassName }} + priorityClassName: {{ .Values.adminConsole.priorityClassName }} + {{- end }} + {{- if .Values.adminConsole.schedulerName }} + schedulerName: {{ .Values.adminConsole.schedulerName }} + {{- end }} + {{- if .Values.adminConsole.hostAliases }} + hostAliases: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.hostAliases "context" $) | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.adminConsole.terminationGracePeriodSeconds }} + {{- if .Values.adminConsole.affinity }} + affinity: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.affinity "context" $) | nindent 8 }} + {{- else if .Values.adminConsole.podAntiAffinityPreset }} + affinity: {{- include "authup.podAntiAffinity" (dict "context" $ "component" "admin-console" "preset" .Values.adminConsole.podAntiAffinityPreset) | nindent 8 }} + {{- end }} + {{- if .Values.adminConsole.nodeSelector }} + nodeSelector: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.adminConsole.tolerations }} + tolerations: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.adminConsole.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "authup.topologySpreadConstraints" (dict "context" $ "component" "admin-console" "constraints" .Values.adminConsole.topologySpreadConstraints) | nindent 8 }} + {{- end }} + {{- if .Values.adminConsole.initContainers }} + initContainers: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.initContainers "context" $) | nindent 8 }} + {{- end }} + containers: + - name: ui + image: {{ include "authup.appImage" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- if .Values.adminConsole.containerSecurityContext.enabled }} + securityContext: {{- include "authup.securityContext" .Values.adminConsole.containerSecurityContext | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- toYaml .Values.diagnosticMode.command | nindent 12 }} + args: {{- toYaml .Values.diagnosticMode.args | nindent 12 }} + {{- else }} + {{- if .Values.adminConsole.command }} + command: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.adminConsole.args }} + args: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.args "context" $) | nindent 12 }} + {{- else }} + args: + - client/admin-console + - start + {{- end }} + {{- end }} + envFrom: + - configMapRef: + name: {{ printf "%s-env" (include "authup.adminConsole.fullname" .) }} + {{- if .Values.adminConsole.extraEnvVarsCM }} + - configMapRef: + name: {{ include "authup.tplvalues.render" (dict "value" .Values.adminConsole.extraEnvVarsCM "context" $) }} + {{- end }} + {{- if .Values.adminConsole.extraEnvVarsSecret }} + - secretRef: + name: {{ include "authup.tplvalues.render" (dict "value" .Values.adminConsole.extraEnvVarsSecret "context" $) }} + {{- end }} + env: + - name: npm_config_cache + value: /tmp/.npm-cache + {{- if .Values.adminConsole.extraEnvVars }} + {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 3000 + protocol: TCP + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.adminConsole.customStartupProbe }} + startupProbe: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.adminConsole.startupProbe.enabled }} + startupProbe: + httpGet: + path: / + port: http + {{- omit .Values.adminConsole.startupProbe "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.adminConsole.customLivenessProbe }} + livenessProbe: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.adminConsole.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: / + port: http + {{- omit .Values.adminConsole.livenessProbe "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.adminConsole.customReadinessProbe }} + readinessProbe: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.adminConsole.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: / + port: http + {{- omit .Values.adminConsole.readinessProbe "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.adminConsole.resources }} + resources: {{- toYaml .Values.adminConsole.resources | nindent 12 }} + {{- end }} + {{- if .Values.adminConsole.lifecycleHooks }} + lifecycle: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + volumeMounts: + - name: tmp + mountPath: /tmp + {{- if .Values.adminConsole.extraVolumeMounts }} + {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.adminConsole.sidecars }} + {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.sidecars "context" $) | nindent 8 }} + {{- end }} + volumes: + - name: tmp + emptyDir: {} + {{- if .Values.adminConsole.extraVolumes }} + {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.extraVolumes "context" $) | nindent 8 }} + {{- end }} +{{- end }} diff --git a/charts/authup/templates/admin-console/hpa.yaml b/charts/authup/templates/admin-console/hpa.yaml new file mode 100644 index 0000000..f8d1322 --- /dev/null +++ b/charts/authup/templates/admin-console/hpa.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.adminConsole.enabled .Values.adminConsole.autoscaling.hpa.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "authup.adminConsole.fullname" . }} + namespace: {{ include "authup.namespace" . | quote }} + labels: {{- include "authup.labels" (dict "context" $ "component" "admin-console") | nindent 4 }} + annotations: {{- include "authup.annotations" (dict "context" $) | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "authup.adminConsole.fullname" . }} + minReplicas: {{ .Values.adminConsole.autoscaling.hpa.minReplicas }} + maxReplicas: {{ .Values.adminConsole.autoscaling.hpa.maxReplicas }} + metrics: + {{- if .Values.adminConsole.autoscaling.hpa.targetCPU }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.adminConsole.autoscaling.hpa.targetCPU }} + {{- end }} + {{- if .Values.adminConsole.autoscaling.hpa.targetMemory }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.adminConsole.autoscaling.hpa.targetMemory }} + {{- end }} +{{- end }} diff --git a/charts/authup/templates/admin-console/httproute.yaml b/charts/authup/templates/admin-console/httproute.yaml new file mode 100644 index 0000000..f3a4916 --- /dev/null +++ b/charts/authup/templates/admin-console/httproute.yaml @@ -0,0 +1,11 @@ +{{- if and .Values.adminConsole.enabled .Values.adminConsole.route.enabled }} +{{- include "authup.httproute" (dict + "context" $ + "component" "admin-console" + "name" (include "authup.adminConsole.fullname" .) + "route" .Values.adminConsole.route + "serviceName" (include "authup.adminConsole.fullname" .) + "servicePort" .Values.adminConsole.service.ports.http + "publicUrl" (include "authup.adminConsole.publicUrl" .) +) }} +{{- end }} diff --git a/charts/authup/templates/admin-console/ingress.yaml b/charts/authup/templates/admin-console/ingress.yaml new file mode 100644 index 0000000..3133b86 --- /dev/null +++ b/charts/authup/templates/admin-console/ingress.yaml @@ -0,0 +1,9 @@ +{{- if and .Values.adminConsole.enabled .Values.adminConsole.ingress.enabled }} +{{- include "authup.ingress" (dict + "context" $ + "component" "admin-console" + "name" (include "authup.adminConsole.fullname" .) + "ingress" .Values.adminConsole.ingress + "serviceName" (include "authup.adminConsole.fullname" .) +) }} +{{- end }} diff --git a/charts/authup/templates/ui/networkpolicy.yaml b/charts/authup/templates/admin-console/networkpolicy.yaml similarity index 58% rename from charts/authup/templates/ui/networkpolicy.yaml rename to charts/authup/templates/admin-console/networkpolicy.yaml index 64dca78..a0ded6a 100644 --- a/charts/authup/templates/ui/networkpolicy.yaml +++ b/charts/authup/templates/admin-console/networkpolicy.yaml @@ -1,14 +1,14 @@ -{{- if and .Values.ui.enabled .Values.ui.networkPolicy.enabled }} +{{- if and .Values.adminConsole.enabled .Values.adminConsole.networkPolicy.enabled }} apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: - name: {{ include "authup.ui.fullname" . }} + name: {{ include "authup.adminConsole.fullname" . }} namespace: {{ include "authup.namespace" . | quote }} - labels: {{- include "authup.labels" (dict "context" $ "component" "ui") | nindent 4 }} + labels: {{- include "authup.labels" (dict "context" $ "component" "admin-console") | nindent 4 }} annotations: {{- include "authup.annotations" (dict "context" $) | nindent 4 }} spec: podSelector: - matchLabels: {{- include "authup.matchLabels" (dict "context" $ "component" "ui") | nindent 6 }} + matchLabels: {{- include "authup.matchLabels" (dict "context" $ "component" "admin-console") | nindent 6 }} policyTypes: - Ingress - Egress @@ -16,25 +16,25 @@ spec: - ports: - port: 3000 protocol: TCP - {{- if not .Values.ui.networkPolicy.allowExternal }} + {{- if not .Values.adminConsole.networkPolicy.allowExternal }} {{- /* An empty `from:` would mean allow-all — always emit at least the same-namespace baseline peer. */}} from: - podSelector: {} - {{- if .Values.ui.networkPolicy.ingressPodMatchLabels }} + {{- if .Values.adminConsole.networkPolicy.ingressPodMatchLabels }} - podSelector: - matchLabels: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.networkPolicy.ingressPodMatchLabels "context" $) | nindent 14 }} + matchLabels: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.networkPolicy.ingressPodMatchLabels "context" $) | nindent 14 }} {{- end }} - {{- if .Values.ui.networkPolicy.ingressNSMatchLabels }} + {{- if .Values.adminConsole.networkPolicy.ingressNSMatchLabels }} - namespaceSelector: - matchLabels: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.networkPolicy.ingressNSMatchLabels "context" $) | nindent 14 }} + matchLabels: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.networkPolicy.ingressNSMatchLabels "context" $) | nindent 14 }} {{- end }} {{- end }} - {{- if .Values.ui.networkPolicy.extraIngress }} - {{- include "authup.tplvalues.render" (dict "value" .Values.ui.networkPolicy.extraIngress "context" $) | nindent 4 }} + {{- if .Values.adminConsole.networkPolicy.extraIngress }} + {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.networkPolicy.extraIngress "context" $) | nindent 4 }} {{- end }} egress: - {{- if .Values.ui.networkPolicy.allowExternalEgress }} + {{- if .Values.adminConsole.networkPolicy.allowExternalEgress }} - {} {{- else }} # DNS @@ -48,7 +48,7 @@ spec: - podSelector: matchLabels: {{- include "authup.matchLabels" (dict "context" $ "component" "server") | nindent 14 }} {{- end }} - {{- if .Values.ui.networkPolicy.extraEgress }} - {{- include "authup.tplvalues.render" (dict "value" .Values.ui.networkPolicy.extraEgress "context" $) | nindent 4 }} + {{- if .Values.adminConsole.networkPolicy.extraEgress }} + {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.networkPolicy.extraEgress "context" $) | nindent 4 }} {{- end }} {{- end }} diff --git a/charts/authup/templates/admin-console/pdb.yaml b/charts/authup/templates/admin-console/pdb.yaml new file mode 100644 index 0000000..aa48ff6 --- /dev/null +++ b/charts/authup/templates/admin-console/pdb.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.adminConsole.enabled .Values.adminConsole.pdb.create }} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "authup.adminConsole.fullname" . }} + namespace: {{ include "authup.namespace" . | quote }} + labels: {{- include "authup.labels" (dict "context" $ "component" "admin-console") | nindent 4 }} + annotations: {{- include "authup.annotations" (dict "context" $) | nindent 4 }} +spec: + {{- if .Values.adminConsole.pdb.minAvailable }} + minAvailable: {{ .Values.adminConsole.pdb.minAvailable }} + {{- end }} + {{- if .Values.adminConsole.pdb.maxUnavailable }} + maxUnavailable: {{ .Values.adminConsole.pdb.maxUnavailable }} + {{- else if not .Values.adminConsole.pdb.minAvailable }} + maxUnavailable: 1 + {{- end }} + selector: + matchLabels: {{- include "authup.matchLabels" (dict "context" $ "component" "admin-console") | nindent 6 }} +{{- end }} diff --git a/charts/authup/templates/admin-console/service.yaml b/charts/authup/templates/admin-console/service.yaml new file mode 100644 index 0000000..88d176f --- /dev/null +++ b/charts/authup/templates/admin-console/service.yaml @@ -0,0 +1,47 @@ +{{- if .Values.adminConsole.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "authup.adminConsole.fullname" . }} + namespace: {{ include "authup.namespace" . | quote }} + labels: {{- include "authup.labels" (dict "context" $ "component" "admin-console") | nindent 4 }} + annotations: + {{- include "authup.annotations" (dict "context" $) | nindent 4 }} + {{- if .Values.adminConsole.service.annotations }} + {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.service.annotations "context" $) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.adminConsole.service.type }} + {{- if and .Values.adminConsole.service.clusterIP (eq .Values.adminConsole.service.type "ClusterIP") }} + clusterIP: {{ .Values.adminConsole.service.clusterIP }} + {{- end }} + {{- if eq .Values.adminConsole.service.type "LoadBalancer" }} + {{- if .Values.adminConsole.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.adminConsole.service.loadBalancerIP }} + {{- end }} + {{- if .Values.adminConsole.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- toYaml .Values.adminConsole.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- end }} + {{- if or (eq .Values.adminConsole.service.type "LoadBalancer") (eq .Values.adminConsole.service.type "NodePort") }} + externalTrafficPolicy: {{ .Values.adminConsole.service.externalTrafficPolicy | quote }} + {{- end }} + {{- if .Values.adminConsole.service.sessionAffinity }} + sessionAffinity: {{ .Values.adminConsole.service.sessionAffinity }} + {{- end }} + {{- if .Values.adminConsole.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- toYaml .Values.adminConsole.service.sessionAffinityConfig | nindent 4 }} + {{- end }} + ports: + - name: http + port: {{ .Values.adminConsole.service.ports.http }} + targetPort: http + protocol: TCP + {{- if and (or (eq .Values.adminConsole.service.type "NodePort") (eq .Values.adminConsole.service.type "LoadBalancer")) .Values.adminConsole.service.nodePorts.http }} + nodePort: {{ .Values.adminConsole.service.nodePorts.http }} + {{- end }} + {{- if .Values.adminConsole.service.extraPorts }} + {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + selector: {{- include "authup.matchLabels" (dict "context" $ "component" "admin-console") | nindent 4 }} +{{- end }} diff --git a/charts/authup/templates/server/networkpolicy.yaml b/charts/authup/templates/server/networkpolicy.yaml index 63452b9..5c05752 100644 --- a/charts/authup/templates/server/networkpolicy.yaml +++ b/charts/authup/templates/server/networkpolicy.yaml @@ -19,7 +19,7 @@ spec: {{- if not .Values.server.networkPolicy.allowExternal }} from: - podSelector: - matchLabels: {{- include "authup.matchLabels" (dict "context" $ "component" "ui") | nindent 14 }} + matchLabels: {{- include "authup.matchLabels" (dict "context" $ "component" "admin-console") | nindent 14 }} {{- if .Values.server.networkPolicy.ingressPodMatchLabels }} - podSelector: matchLabels: {{- include "authup.tplvalues.render" (dict "value" .Values.server.networkPolicy.ingressPodMatchLabels "context" $) | nindent 14 }} diff --git a/charts/authup/templates/ui/deployment.yaml b/charts/authup/templates/ui/deployment.yaml deleted file mode 100644 index bcb2e3d..0000000 --- a/charts/authup/templates/ui/deployment.yaml +++ /dev/null @@ -1,159 +0,0 @@ -{{- if .Values.ui.enabled }} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "authup.ui.fullname" . }} - namespace: {{ include "authup.namespace" . | quote }} - labels: {{- include "authup.labels" (dict "context" $ "component" "ui") | nindent 4 }} - annotations: {{- include "authup.annotations" (dict "context" $) | nindent 4 }} -spec: - {{- if not .Values.ui.autoscaling.hpa.enabled }} - replicas: {{ .Values.ui.replicaCount }} - {{- end }} - revisionHistoryLimit: {{ .Values.ui.revisionHistoryLimit }} - {{- if .Values.ui.updateStrategy }} - strategy: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.updateStrategy "context" $) | nindent 4 }} - {{- end }} - selector: - matchLabels: {{- include "authup.matchLabels" (dict "context" $ "component" "ui") | nindent 6 }} - template: - metadata: - labels: {{- include "authup.labels" (dict "context" $ "component" "ui") | nindent 8 }} - {{- if .Values.ui.podLabels }} - {{- include "authup.tplvalues.render" (dict "value" .Values.ui.podLabels "context" $) | nindent 8 }} - {{- end }} - annotations: - {{- if not .Values.ui.disableRestartOnChanges }} - checksum/env: {{ include "authup.ui.configEnv" . | sha256sum }} - {{- end }} - {{- if .Values.ui.podAnnotations }} - {{- include "authup.tplvalues.render" (dict "value" .Values.ui.podAnnotations "context" $) | nindent 8 }} - {{- end }} - spec: - serviceAccountName: {{ include "authup.serviceAccountName" . }} - automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} - {{- include "authup.imagePullSecrets" . | nindent 6 }} - {{- if .Values.ui.podSecurityContext.enabled }} - securityContext: {{- include "authup.securityContext" .Values.ui.podSecurityContext | nindent 8 }} - {{- end }} - {{- if .Values.ui.priorityClassName }} - priorityClassName: {{ .Values.ui.priorityClassName }} - {{- end }} - {{- if .Values.ui.schedulerName }} - schedulerName: {{ .Values.ui.schedulerName }} - {{- end }} - {{- if .Values.ui.hostAliases }} - hostAliases: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.hostAliases "context" $) | nindent 8 }} - {{- end }} - terminationGracePeriodSeconds: {{ .Values.ui.terminationGracePeriodSeconds }} - {{- if .Values.ui.affinity }} - affinity: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.affinity "context" $) | nindent 8 }} - {{- else if .Values.ui.podAntiAffinityPreset }} - affinity: {{- include "authup.podAntiAffinity" (dict "context" $ "component" "ui" "preset" .Values.ui.podAntiAffinityPreset) | nindent 8 }} - {{- end }} - {{- if .Values.ui.nodeSelector }} - nodeSelector: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.nodeSelector "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.ui.tolerations }} - tolerations: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.tolerations "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.ui.topologySpreadConstraints }} - topologySpreadConstraints: {{- include "authup.topologySpreadConstraints" (dict "context" $ "component" "ui" "constraints" .Values.ui.topologySpreadConstraints) | nindent 8 }} - {{- end }} - {{- if .Values.ui.initContainers }} - initContainers: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.initContainers "context" $) | nindent 8 }} - {{- end }} - containers: - - name: ui - image: {{ include "authup.appImage" . }} - imagePullPolicy: {{ .Values.image.pullPolicy }} - {{- if .Values.ui.containerSecurityContext.enabled }} - securityContext: {{- include "authup.securityContext" .Values.ui.containerSecurityContext | nindent 12 }} - {{- end }} - {{- if .Values.diagnosticMode.enabled }} - command: {{- toYaml .Values.diagnosticMode.command | nindent 12 }} - args: {{- toYaml .Values.diagnosticMode.args | nindent 12 }} - {{- else }} - {{- if .Values.ui.command }} - command: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.command "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.ui.args }} - args: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.args "context" $) | nindent 12 }} - {{- else }} - args: - - client/console - - start - {{- end }} - {{- end }} - envFrom: - - configMapRef: - name: {{ printf "%s-env" (include "authup.ui.fullname" .) }} - {{- if .Values.ui.extraEnvVarsCM }} - - configMapRef: - name: {{ include "authup.tplvalues.render" (dict "value" .Values.ui.extraEnvVarsCM "context" $) }} - {{- end }} - {{- if .Values.ui.extraEnvVarsSecret }} - - secretRef: - name: {{ include "authup.tplvalues.render" (dict "value" .Values.ui.extraEnvVarsSecret "context" $) }} - {{- end }} - env: - - name: npm_config_cache - value: /tmp/.npm-cache - {{- if .Values.ui.extraEnvVars }} - {{- include "authup.tplvalues.render" (dict "value" .Values.ui.extraEnvVars "context" $) | nindent 12 }} - {{- end }} - ports: - - name: http - containerPort: 3000 - protocol: TCP - {{- if not .Values.diagnosticMode.enabled }} - {{- if .Values.ui.customStartupProbe }} - startupProbe: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.customStartupProbe "context" $) | nindent 12 }} - {{- else if .Values.ui.startupProbe.enabled }} - startupProbe: - httpGet: - path: / - port: http - {{- omit .Values.ui.startupProbe "enabled" | toYaml | nindent 12 }} - {{- end }} - {{- if .Values.ui.customLivenessProbe }} - livenessProbe: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.customLivenessProbe "context" $) | nindent 12 }} - {{- else if .Values.ui.livenessProbe.enabled }} - livenessProbe: - httpGet: - path: / - port: http - {{- omit .Values.ui.livenessProbe "enabled" | toYaml | nindent 12 }} - {{- end }} - {{- if .Values.ui.customReadinessProbe }} - readinessProbe: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.customReadinessProbe "context" $) | nindent 12 }} - {{- else if .Values.ui.readinessProbe.enabled }} - readinessProbe: - httpGet: - path: / - port: http - {{- omit .Values.ui.readinessProbe "enabled" | toYaml | nindent 12 }} - {{- end }} - {{- end }} - {{- if .Values.ui.resources }} - resources: {{- toYaml .Values.ui.resources | nindent 12 }} - {{- end }} - {{- if .Values.ui.lifecycleHooks }} - lifecycle: {{- include "authup.tplvalues.render" (dict "value" .Values.ui.lifecycleHooks "context" $) | nindent 12 }} - {{- end }} - volumeMounts: - - name: tmp - mountPath: /tmp - {{- if .Values.ui.extraVolumeMounts }} - {{- include "authup.tplvalues.render" (dict "value" .Values.ui.extraVolumeMounts "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.ui.sidecars }} - {{- include "authup.tplvalues.render" (dict "value" .Values.ui.sidecars "context" $) | nindent 8 }} - {{- end }} - volumes: - - name: tmp - emptyDir: {} - {{- if .Values.ui.extraVolumes }} - {{- include "authup.tplvalues.render" (dict "value" .Values.ui.extraVolumes "context" $) | nindent 8 }} - {{- end }} -{{- end }} diff --git a/charts/authup/templates/ui/hpa.yaml b/charts/authup/templates/ui/hpa.yaml deleted file mode 100644 index c6d400f..0000000 --- a/charts/authup/templates/ui/hpa.yaml +++ /dev/null @@ -1,33 +0,0 @@ -{{- if and .Values.ui.enabled .Values.ui.autoscaling.hpa.enabled }} -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include "authup.ui.fullname" . }} - namespace: {{ include "authup.namespace" . | quote }} - labels: {{- include "authup.labels" (dict "context" $ "component" "ui") | nindent 4 }} - annotations: {{- include "authup.annotations" (dict "context" $) | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "authup.ui.fullname" . }} - minReplicas: {{ .Values.ui.autoscaling.hpa.minReplicas }} - maxReplicas: {{ .Values.ui.autoscaling.hpa.maxReplicas }} - metrics: - {{- if .Values.ui.autoscaling.hpa.targetCPU }} - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: {{ .Values.ui.autoscaling.hpa.targetCPU }} - {{- end }} - {{- if .Values.ui.autoscaling.hpa.targetMemory }} - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: {{ .Values.ui.autoscaling.hpa.targetMemory }} - {{- end }} -{{- end }} diff --git a/charts/authup/templates/ui/httproute.yaml b/charts/authup/templates/ui/httproute.yaml deleted file mode 100644 index 3a2aecd..0000000 --- a/charts/authup/templates/ui/httproute.yaml +++ /dev/null @@ -1,11 +0,0 @@ -{{- if and .Values.ui.enabled .Values.ui.route.enabled }} -{{- include "authup.httproute" (dict - "context" $ - "component" "ui" - "name" (include "authup.ui.fullname" .) - "route" .Values.ui.route - "serviceName" (include "authup.ui.fullname" .) - "servicePort" .Values.ui.service.ports.http - "publicUrl" (include "authup.ui.publicUrl" .) -) }} -{{- end }} diff --git a/charts/authup/templates/ui/ingress.yaml b/charts/authup/templates/ui/ingress.yaml deleted file mode 100644 index c9429e2..0000000 --- a/charts/authup/templates/ui/ingress.yaml +++ /dev/null @@ -1,9 +0,0 @@ -{{- if and .Values.ui.enabled .Values.ui.ingress.enabled }} -{{- include "authup.ingress" (dict - "context" $ - "component" "ui" - "name" (include "authup.ui.fullname" .) - "ingress" .Values.ui.ingress - "serviceName" (include "authup.ui.fullname" .) -) }} -{{- end }} diff --git a/charts/authup/templates/ui/pdb.yaml b/charts/authup/templates/ui/pdb.yaml deleted file mode 100644 index 66165cc..0000000 --- a/charts/authup/templates/ui/pdb.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- if and .Values.ui.enabled .Values.ui.pdb.create }} -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: {{ include "authup.ui.fullname" . }} - namespace: {{ include "authup.namespace" . | quote }} - labels: {{- include "authup.labels" (dict "context" $ "component" "ui") | nindent 4 }} - annotations: {{- include "authup.annotations" (dict "context" $) | nindent 4 }} -spec: - {{- if .Values.ui.pdb.minAvailable }} - minAvailable: {{ .Values.ui.pdb.minAvailable }} - {{- end }} - {{- if .Values.ui.pdb.maxUnavailable }} - maxUnavailable: {{ .Values.ui.pdb.maxUnavailable }} - {{- else if not .Values.ui.pdb.minAvailable }} - maxUnavailable: 1 - {{- end }} - selector: - matchLabels: {{- include "authup.matchLabels" (dict "context" $ "component" "ui") | nindent 6 }} -{{- end }} diff --git a/charts/authup/templates/ui/service.yaml b/charts/authup/templates/ui/service.yaml deleted file mode 100644 index ee03fd1..0000000 --- a/charts/authup/templates/ui/service.yaml +++ /dev/null @@ -1,47 +0,0 @@ -{{- if .Values.ui.enabled }} -apiVersion: v1 -kind: Service -metadata: - name: {{ include "authup.ui.fullname" . }} - namespace: {{ include "authup.namespace" . | quote }} - labels: {{- include "authup.labels" (dict "context" $ "component" "ui") | nindent 4 }} - annotations: - {{- include "authup.annotations" (dict "context" $) | nindent 4 }} - {{- if .Values.ui.service.annotations }} - {{- include "authup.tplvalues.render" (dict "value" .Values.ui.service.annotations "context" $) | nindent 4 }} - {{- end }} -spec: - type: {{ .Values.ui.service.type }} - {{- if and .Values.ui.service.clusterIP (eq .Values.ui.service.type "ClusterIP") }} - clusterIP: {{ .Values.ui.service.clusterIP }} - {{- end }} - {{- if eq .Values.ui.service.type "LoadBalancer" }} - {{- if .Values.ui.service.loadBalancerIP }} - loadBalancerIP: {{ .Values.ui.service.loadBalancerIP }} - {{- end }} - {{- if .Values.ui.service.loadBalancerSourceRanges }} - loadBalancerSourceRanges: {{- toYaml .Values.ui.service.loadBalancerSourceRanges | nindent 4 }} - {{- end }} - {{- end }} - {{- if or (eq .Values.ui.service.type "LoadBalancer") (eq .Values.ui.service.type "NodePort") }} - externalTrafficPolicy: {{ .Values.ui.service.externalTrafficPolicy | quote }} - {{- end }} - {{- if .Values.ui.service.sessionAffinity }} - sessionAffinity: {{ .Values.ui.service.sessionAffinity }} - {{- end }} - {{- if .Values.ui.service.sessionAffinityConfig }} - sessionAffinityConfig: {{- toYaml .Values.ui.service.sessionAffinityConfig | nindent 4 }} - {{- end }} - ports: - - name: http - port: {{ .Values.ui.service.ports.http }} - targetPort: http - protocol: TCP - {{- if and (or (eq .Values.ui.service.type "NodePort") (eq .Values.ui.service.type "LoadBalancer")) .Values.ui.service.nodePorts.http }} - nodePort: {{ .Values.ui.service.nodePorts.http }} - {{- end }} - {{- if .Values.ui.service.extraPorts }} - {{- include "authup.tplvalues.render" (dict "value" .Values.ui.service.extraPorts "context" $) | nindent 4 }} - {{- end }} - selector: {{- include "authup.matchLabels" (dict "context" $ "component" "ui") | nindent 4 }} -{{- end }} diff --git a/charts/authup/templates/validations.yaml b/charts/authup/templates/validations.yaml index a12b5f1..dbbbed6 100644 --- a/charts/authup/templates/validations.yaml +++ b/charts/authup/templates/validations.yaml @@ -34,8 +34,8 @@ store, token blocklist and MFA challenges fall back to a per-process memory cach {{- fail "authup: auth.existingSecret and inline auth values are mutually exclusive — when an existing secret is referenced, the inline adminPassword / systemClientSecret / secretsEncryptionKey values are unused; remove them to avoid a false sense of configuration." }} {{- end }} -{{- if and .Values.ui.enabled (not .Values.server.enabled) (not .Values.ui.apiUrl) }} -{{- fail "authup: ui.enabled without server.enabled requires ui.apiUrl (the browser-reachable URL of an external authup server-core)." }} +{{- if and .Values.adminConsole.enabled (not .Values.server.enabled) (not .Values.adminConsole.apiUrl) }} +{{- fail "authup: adminConsole.enabled without server.enabled requires adminConsole.apiUrl (the browser-reachable URL of an external authup server-core)." }} {{- end }} {{/* An external database password must be deliberate — never silently generated for a database the chart does not own. */}} @@ -46,12 +46,12 @@ store, token blocklist and MFA challenges fall back to a per-process memory cach {{- if and .Values.server.ingress.enabled (not .Values.server.ingress.hostname) (not .Values.server.ingress.extraHosts) (not .Values.server.ingress.extraRules) }} {{- fail "authup: server.ingress.enabled requires server.ingress.hostname (or extraHosts / extraRules)." }} {{- end }} -{{- if and .Values.ui.enabled .Values.ui.ingress.enabled (not .Values.ui.ingress.hostname) (not .Values.ui.ingress.extraHosts) (not .Values.ui.ingress.extraRules) }} -{{- fail "authup: ui.ingress.enabled requires ui.ingress.hostname (or extraHosts / extraRules)." }} +{{- if and .Values.adminConsole.enabled .Values.adminConsole.ingress.enabled (not .Values.adminConsole.ingress.hostname) (not .Values.adminConsole.ingress.extraHosts) (not .Values.adminConsole.ingress.extraRules) }} +{{- fail "authup: adminConsole.ingress.enabled requires adminConsole.ingress.hostname (or extraHosts / extraRules)." }} {{- 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 "ui.publicUrl" .Values.ui.publicUrl "ui.apiUrl" .Values.ui.apiUrl "ui.internalApiUrl" .Values.ui.internalApiUrl }} +{{- 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)) }} {{- fail (printf "authup: %s must be a full URL including the http(s):// scheme (got %q)." $key $value) }} {{- end }} diff --git a/charts/authup/values.schema.json b/charts/authup/values.schema.json index 2185609..1d15b12 100644 --- a/charts/authup/values.schema.json +++ b/charts/authup/values.schema.json @@ -2,2001 +2,1722 @@ "$schema": "http://json-schema.org/draft-07/schema#", "additionalProperties": false, "properties": { - "auth": { + "adminConsole": { "additionalProperties": false, - "description": "#\n# Credentials seeded into authup at boot. Empty passwords are generated once and\n# kept stable across upgrades via lookup.\n# GITOPS WARNING: lookup is inert under `helm template` and ArgoCD-style\n# renders — every sync would then apply a NEW random value, roll the pods via\n# the checksum annotation, and desync the Secret from the live credential\n# (authup only applies the admin password at first provisioning unless the\n# reset flag is set). GitOps users MUST set explicit values or auth.existingSecret.", + "description": "#", "properties": { - "adminPassword": { + "affinity": { + "additionalProperties": true, + "description": "Affinity (overrides the anti-affinity preset when set)", + "required": [], + "title": "affinity" + }, + "apiUrl": { "default": "", - "description": "Initial admin user password (\"\" = generate once, keep across upgrades).\nChanging it after the first install only takes effect with\nadminPasswordReset=true for one upgrade cycle", + "description": "Browser-facing server-core URL (NUXT_PUBLIC_API_URL). \"\" = the server public URL.\nMust be reachable from the user's browser, never a cluster-internal DNS name", "required": [], - "title": "adminPassword", + "title": "apiUrl", "type": "string" }, - "adminPasswordReset": { - "default": false, - "description": "Re-assert the admin password on every boot (USER_ADMIN_PASSWORD_RESET)", + "args": { + "description": "Override the container args", + "items": { + "required": [] + }, "required": [], - "title": "adminPasswordReset", - "type": "boolean" + "title": "args", + "type": "array" }, - "existingSecret": { - "default": "", - "description": "Existing secret holding the keys below instead of the chart-managed secret (tpl-rendered)", + "autoscaling": { + "additionalProperties": false, + "properties": { + "hpa": { + "additionalProperties": false, + "properties": { + "enabled": { + "default": false, + "description": "Enable HPA for the UI", + "required": [], + "title": "enabled", + "type": "boolean" + }, + "maxReplicas": { + "default": 5, + "description": "Maximum replicas", + "required": [], + "title": "maxReplicas", + "type": "integer" + }, + "minReplicas": { + "default": 2, + "description": "Minimum replicas", + "required": [], + "title": "minReplicas", + "type": "integer" + }, + "targetCPU": { + "default": 75, + "description": "Target CPU utilization percentage", + "required": [], + "title": "targetCPU", + "type": "integer" + }, + "targetMemory": { + "default": "", + "description": "Target memory utilization percentage", + "required": [], + "title": "targetMemory", + "type": "string" + } + }, + "required": [ + "enabled", + "minReplicas", + "maxReplicas", + "targetCPU", + "targetMemory" + ], + "title": "hpa", + "type": "object" + } + }, + "required": [ + "hpa" + ], + "title": "autoscaling", + "type": "object" + }, + "command": { + "description": "Override the container command", + "items": { + "required": [] + }, "required": [], - "title": "existingSecret", - "type": "string" + "title": "command", + "type": "array" }, - "secretKeys": { - "additionalProperties": false, + "config": { + "additionalProperties": true, + "description": "Extra environment variables rendered literally into the env ConfigMap", + "required": [], + "title": "config" + }, + "containerSecurityContext": { + "additionalProperties": true, + "description": "Container security context (same root-image caveat as the server)", "properties": { - "adminPasswordKey": { - "default": "admin-password", - "description": "Key inside the (existing) secret holding the admin password", + "allowPrivilegeEscalation": { + "default": false, "required": [], - "title": "adminPasswordKey", - "type": "string" + "title": "allowPrivilegeEscalation", + "type": "boolean" }, - "secretsEncryptionKeyKey": { - "default": "secrets-encryption-key", - "description": "Key inside the (existing) secret holding the secrets encryption key", + "capabilities": { + "additionalProperties": false, + "properties": { + "drop": { + "items": { + "anyOf": [ + { + "required": [], + "type": "string" + } + ], + "required": [] + }, + "required": [], + "title": "drop", + "type": "array" + } + }, + "required": [ + "drop" + ], + "title": "capabilities", + "type": "object" + }, + "enabled": { + "default": true, "required": [], - "title": "secretsEncryptionKeyKey", - "type": "string" + "title": "enabled", + "type": "boolean" }, - "systemClientSecretKey": { - "default": "system-client-secret", - "description": "Key inside the (existing) secret holding the system client secret", + "readOnlyRootFilesystem": { + "default": false, "required": [], - "title": "systemClientSecretKey", - "type": "string" + "title": "readOnlyRootFilesystem", + "type": "boolean" + }, + "runAsNonRoot": { + "default": false, + "required": [], + "title": "runAsNonRoot", + "type": "boolean" + }, + "runAsUser": { + "default": 0, + "required": [], + "title": "runAsUser", + "type": "integer" + }, + "seccompProfile": { + "additionalProperties": false, + "properties": { + "type": { + "default": "RuntimeDefault", + "required": [], + "title": "type", + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "seccompProfile", + "type": "object" } }, "required": [ - "adminPasswordKey", - "systemClientSecretKey", - "secretsEncryptionKeyKey" + "enabled", + "runAsUser", + "runAsNonRoot", + "allowPrivilegeEscalation", + "capabilities", + "seccompProfile", + "readOnlyRootFilesystem" ], - "title": "secretKeys", - "type": "object" + "title": "containerSecurityContext" }, - "secretsEncryptionKey": { - "default": "", - "description": "Optional AES-256 key-encryption-key wrapping the realm key store\n(SECRETS_ENCRYPTION_KEY, base64-encoded 32 bytes). Deliberately never\ngenerated by the chart: this key is effectively write-once — losing or\nrotating it bricks wrapped MFA seeds and signing keys. Set it explicitly\nhere, or reference it via existingSecret plus\nsecretsEncryptionKeyEnabled=true, and back it up.", + "customLivenessProbe": { + "additionalProperties": true, + "description": "Custom liveness probe", "required": [], - "title": "secretsEncryptionKey", - "type": "string" + "title": "customLivenessProbe" }, - "secretsEncryptionKeyEnabled": { + "customReadinessProbe": { + "additionalProperties": true, + "description": "Custom readiness probe", + "required": [], + "title": "customReadinessProbe" + }, + "customStartupProbe": { + "additionalProperties": true, + "description": "Custom startup probe", + "required": [], + "title": "customStartupProbe" + }, + "disableRestartOnChanges": { "default": false, - "description": "Declare that auth.existingSecret carries the secrets encryption key\n(under secretKeys.secretsEncryptionKeyKey). Required for the key to be\nwired from an existing secret — it is never inferred, so a missing key can\nnot silently fail open into plaintext-at-rest", + "description": "Disable the checksum annotations that roll pods on config changes", "required": [], - "title": "secretsEncryptionKeyEnabled", + "title": "disableRestartOnChanges", "type": "boolean" }, - "systemClientEnabled": { - "default": false, - "description": "Provision the built-in system client (CLIENT_SYSTEM_ENABLED); required for machine-to-machine consumers", + "enabled": { + "default": true, + "description": "Deploy the client-admin-console admin UI (false = headless IdP)", "required": [], - "title": "systemClientEnabled", + "title": "enabled", "type": "boolean" }, - "systemClientSecret": { + "extraEnvVars": { + "description": "Extra environment variables for the UI container", + "items": { + "required": [] + }, + "required": [], + "title": "extraEnvVars", + "type": "array" + }, + "extraEnvVarsCM": { "default": "", - "description": "System client secret (\"\" = generate once when systemClientEnabled)", + "description": "Extra ConfigMap with environment variables (tpl-rendered name)", "required": [], - "title": "systemClientSecret", + "title": "extraEnvVarsCM", "type": "string" }, - "systemClientSecretReset": { - "default": false, - "description": "Re-assert the system client secret on every boot (CLIENT_SYSTEM_SECRET_RESET)", + "extraEnvVarsSecret": { + "default": "", + "description": "Extra Secret with environment variables (tpl-rendered name)", "required": [], - "title": "systemClientSecretReset", - "type": "boolean" - } - }, - "required": [ - "adminPassword", - "adminPasswordReset", - "systemClientEnabled", - "systemClientSecret", - "systemClientSecretReset", - "secretsEncryptionKey", - "secretsEncryptionKeyEnabled", - "existingSecret", - "secretKeys" - ], - "title": "auth", - "type": "object" - }, - "commonAnnotations": { - "additionalProperties": true, - "description": "Annotations added to every object", - "required": [], - "title": "commonAnnotations" - }, - "commonLabels": { - "additionalProperties": true, - "description": "Labels added to every object", - "required": [], - "title": "commonLabels" - }, - "database": { - "additionalProperties": false, - "description": "#\n# Exactly one of: built-in postgresql, built-in mysql, or externalDatabase.", - "properties": { - "type": { - "default": "postgres", - "description": "Database engine when using externalDatabase: postgres or mysql", - "required": [], - "title": "type", + "title": "extraEnvVarsSecret", "type": "string" - } - }, - "required": [ - "type" - ], - "title": "database", - "type": "object" - }, - "diagnosticMode": { - "additionalProperties": false, - "properties": { - "args": { - "description": "Args overriding the containers' args in diagnostic mode", + }, + "extraVolumeMounts": { + "description": "Extra volume mounts (tpl-rendered)", "items": { - "anyOf": [ - { - "required": [], - "type": "string" - } - ], "required": [] }, "required": [], - "title": "args", + "title": "extraVolumeMounts", "type": "array" }, - "command": { - "description": "Command overriding the containers' command in diagnostic mode", + "extraVolumes": { + "description": "Extra volumes (tpl-rendered)", "items": { - "anyOf": [ - { - "required": [], - "type": "string" - } - ], "required": [] }, "required": [], - "title": "command", + "title": "extraVolumes", "type": "array" }, - "enabled": { - "default": false, - "description": "Start every container with a sleep command and disable probes (debugging)", - "required": [], - "title": "enabled", - "type": "boolean" - } - }, - "required": [ - "enabled", - "command", - "args" - ], - "title": "diagnosticMode", - "type": "object" - }, - "externalDatabase": { - "additionalProperties": false, - "properties": { - "database": { - "default": "authup", - "description": "External database name (must exist, or the user needs CREATE privilege)", + "hostAliases": { + "description": "Pod host aliases", + "items": { + "required": [] + }, "required": [], - "title": "database", - "type": "string" + "title": "hostAliases", + "type": "array" }, - "existingSecret": { - "default": "", - "description": "Existing secret with the database password (tpl-rendered)", - "required": [], - "title": "existingSecret", - "type": "string" + "ingress": { + "additionalProperties": false, + "properties": { + "annotations": { + "additionalProperties": true, + "description": "Ingress annotations (tpl-rendered)", + "required": [], + "title": "annotations" + }, + "certManager": { + "default": false, + "description": "Request a cert-manager certificate (adds kubernetes.io/tls-acme)", + "required": [], + "title": "certManager", + "type": "boolean" + }, + "enabled": { + "default": false, + "description": "Enable ingress for the UI", + "required": [], + "title": "enabled", + "type": "boolean" + }, + "extraHosts": { + "description": "Extra hosts", + "items": { + "required": [] + }, + "required": [], + "title": "extraHosts", + "type": "array" + }, + "extraPaths": { + "description": "Extra paths for the primary host", + "items": { + "required": [] + }, + "required": [], + "title": "extraPaths", + "type": "array" + }, + "extraRules": { + "description": "Full custom rules (tpl-rendered; overrides the generated rule)", + "items": { + "required": [] + }, + "required": [], + "title": "extraRules", + "type": "array" + }, + "extraTls": { + "description": "Extra TLS entries", + "items": { + "required": [] + }, + "required": [], + "title": "extraTls", + "type": "array" + }, + "hostname": { + "default": "", + "description": "Ingress hostname (tpl-rendered); also drives the derived UI public URL", + "required": [], + "title": "hostname", + "type": "string" + }, + "ingressClassName": { + "default": "", + "description": "Ingress class name", + "required": [], + "title": "ingressClassName", + "type": "string" + }, + "path": { + "default": "/", + "description": "Ingress path", + "required": [], + "title": "path", + "type": "string" + }, + "pathType": { + "default": "Prefix", + "description": "Ingress path type", + "required": [], + "title": "pathType", + "type": "string" + }, + "tls": { + "default": false, + "description": "Enable TLS for the hostname", + "required": [], + "title": "tls", + "type": "boolean" + } + }, + "required": [ + "enabled", + "hostname", + "path", + "pathType", + "ingressClassName", + "tls", + "certManager", + "extraHosts", + "extraPaths", + "extraTls", + "extraRules" + ], + "title": "ingress", + "type": "object" }, - "existingSecretPasswordKey": { - "default": "password", - "description": "Key inside externalDatabase.existingSecret holding the password", + "initContainers": { + "description": "Init containers (tpl-rendered)", + "items": { + "required": [] + }, "required": [], - "title": "existingSecretPasswordKey", - "type": "string" + "title": "initContainers", + "type": "array" }, - "host": { + "internalApiUrl": { "default": "", - "description": "External database host (tpl-rendered). Setting this selects the external database", + "description": "Server-side (SSR) API URL override (NUXT_API_URL), e.g. the in-cluster\nservice URL to keep SSR traffic off the ingress", "required": [], - "title": "host", + "title": "internalApiUrl", "type": "string" }, - "password": { - "default": "", - "description": "External database password (stored in a chart-managed secret, never inline env)", + "lifecycleHooks": { + "additionalProperties": true, + "description": "Container lifecycle hooks", "required": [], - "title": "password", - "type": "string" + "title": "lifecycleHooks" }, - "port": { - "default": "", - "description": "External database port (\"\" = engine default: 5432 / 3306)", - "required": [], - "title": "port", - "type": "string" + "livenessProbe": { + "additionalProperties": false, + "properties": { + "enabled": { + "default": true, + "description": "Enable the liveness probe", + "required": [], + "title": "enabled", + "type": "boolean" + }, + "failureThreshold": { + "default": 3, + "required": [], + "title": "failureThreshold", + "type": "integer" + }, + "initialDelaySeconds": { + "default": 0, + "required": [], + "title": "initialDelaySeconds", + "type": "integer" + }, + "periodSeconds": { + "default": 30, + "required": [], + "title": "periodSeconds", + "type": "integer" + }, + "successThreshold": { + "default": 1, + "required": [], + "title": "successThreshold", + "type": "integer" + }, + "timeoutSeconds": { + "default": 5, + "required": [], + "title": "timeoutSeconds", + "type": "integer" + } + }, + "required": [ + "enabled", + "initialDelaySeconds", + "periodSeconds", + "timeoutSeconds", + "failureThreshold", + "successThreshold" + ], + "title": "livenessProbe", + "type": "object" }, - "user": { - "default": "authup", - "description": "External database user", + "networkPolicy": { + "additionalProperties": false, + "properties": { + "allowExternal": { + "default": true, + "description": "Allow ingress from anywhere", + "required": [], + "title": "allowExternal", + "type": "boolean" + }, + "allowExternalEgress": { + "default": true, + "description": "Allow all egress", + "required": [], + "title": "allowExternalEgress", + "type": "boolean" + }, + "enabled": { + "default": false, + "description": "Create a NetworkPolicy for the UI", + "required": [], + "title": "enabled", + "type": "boolean" + }, + "extraEgress": { + "description": "Extra egress rules", + "items": { + "required": [] + }, + "required": [], + "title": "extraEgress", + "type": "array" + }, + "extraIngress": { + "description": "Extra ingress rules", + "items": { + "required": [] + }, + "required": [], + "title": "extraIngress", + "type": "array" + }, + "ingressNSMatchLabels": { + "additionalProperties": true, + "description": "Namespace labels allowed to connect when allowExternal is false", + "required": [], + "title": "ingressNSMatchLabels" + }, + "ingressPodMatchLabels": { + "additionalProperties": true, + "description": "Pod labels allowed to connect when allowExternal is false", + "required": [], + "title": "ingressPodMatchLabels" + } + }, + "required": [ + "enabled", + "allowExternal", + "allowExternalEgress", + "extraIngress", + "extraEgress" + ], + "title": "networkPolicy", + "type": "object" + }, + "nodeSelector": { + "additionalProperties": true, + "description": "Node selector", "required": [], - "title": "user", - "type": "string" - } - }, - "required": [ - "host", - "port", - "user", - "database", - "password", - "existingSecret", - "existingSecretPasswordKey" - ], - "title": "externalDatabase", - "type": "object" - }, - "externalRedis": { - "additionalProperties": false, - "properties": { - "existingSecret": { - "default": "", - "description": "Existing secret holding a FULL connection URL (tpl-rendered)", + "title": "nodeSelector" + }, + "pdb": { + "additionalProperties": false, + "properties": { + "create": { + "default": false, + "description": "Create a PodDisruptionBudget for the UI", + "required": [], + "title": "create", + "type": "boolean" + }, + "maxUnavailable": { + "default": "", + "description": "Maximum unavailable pods (defaults to 1 when both are empty)", + "required": [], + "title": "maxUnavailable", + "type": "string" + }, + "minAvailable": { + "default": "", + "description": "Minimum available pods", + "required": [], + "title": "minAvailable", + "type": "string" + } + }, + "required": [ + "create", + "minAvailable", + "maxUnavailable" + ], + "title": "pdb", + "type": "object" + }, + "podAnnotations": { + "additionalProperties": true, + "description": "Pod annotations (tpl-rendered)", "required": [], - "title": "existingSecret", - "type": "string" + "title": "podAnnotations" }, - "existingSecretKey": { - "default": "redis-connection-string", - "description": "Key inside externalRedis.existingSecret holding the connection URL", + "podAntiAffinityPreset": { + "default": "soft", + "description": "Pod anti-affinity preset: soft, hard or \"\"", "required": [], - "title": "existingSecretKey", + "title": "podAntiAffinityPreset", "type": "string" }, - "host": { - "default": "", - "description": "External Redis host (alternative to url; the chart composes the URL)", + "podLabels": { + "additionalProperties": true, + "description": "Pod labels (tpl-rendered)", "required": [], - "title": "host", - "type": "string" + "title": "podLabels" }, - "password": { + "podSecurityContext": { + "additionalProperties": true, + "description": "Pod security context", + "properties": { + "enabled": { + "default": true, + "required": [], + "title": "enabled", + "type": "boolean" + }, + "fsGroup": { + "default": 1000, + "required": [], + "title": "fsGroup", + "type": "integer" + } + }, + "required": [ + "enabled", + "fsGroup" + ], + "title": "podSecurityContext" + }, + "priorityClassName": { "default": "", - "description": "External Redis password", + "description": "Priority class name", "required": [], - "title": "password", + "title": "priorityClassName", "type": "string" }, - "port": { - "default": 6379, - "description": "External Redis port", - "required": [], - "title": "port", - "type": "integer" - }, - "url": { + "publicUrl": { "default": "", - "description": "Full external Redis/Valkey connection URL (redis://[user:pass@]host:port); stored in a chart-managed secret", + "description": "Public URL of the UI (NUXT_PUBLIC_PUBLIC_URL). \"\" = derived from adminConsole.ingress", "required": [], - "title": "url", + "title": "publicUrl", "type": "string" - } - }, - "required": [ - "url", - "host", - "port", - "password", - "existingSecret", - "existingSecretKey" - ], - "title": "externalRedis", - "type": "object" - }, - "extraDeploy": { - "description": "Extra objects to deploy (rendered through tpl; list of manifests or strings)", - "items": { - "required": [] - }, - "required": [], - "title": "extraDeploy", - "type": "array" - }, - "fullnameOverride": { - "default": "", - "description": "Override the fully qualified release name", - "required": [], - "title": "fullnameOverride", - "type": "string" - }, - "global": { - "additionalProperties": false, - "properties": { - "defaultStorageClass": { - "default": "", - "description": "Global default storage class for dynamic provisioning", - "required": [], - "title": "defaultStorageClass", - "type": "string" - }, - "imagePullSecrets": { - "description": "Global image pull secrets (list of names or objects)", - "items": { - "required": [] - }, - "required": [], - "title": "imagePullSecrets", - "type": "array" - }, - "imageRegistry": { - "default": "", - "description": "Global container image registry override (takes precedence over image.registry)", - "required": [], - "title": "imageRegistry", - "type": "string" - } - }, - "required": [ - "imageRegistry", - "imagePullSecrets", - "defaultStorageClass" - ], - "title": "global", - "type": "object" - }, - "image": { - "additionalProperties": false, - "description": "#", - "properties": { - "digest": { - "default": "", - "description": "Authup image digest (takes precedence over tag)", - "required": [], - "title": "digest", - "type": "string" - }, - "pullPolicy": { - "default": "IfNotPresent", - "description": "Authup image pull policy", - "required": [], - "title": "pullPolicy", - "type": "string" - }, - "pullSecrets": { - "description": "Authup image pull secrets", - "items": { - "required": [] - }, - "required": [], - "title": "pullSecrets", - "type": "array" - }, - "registry": { - "default": "docker.io", - "description": "Authup image registry", - "required": [], - "title": "registry", - "type": "string" - }, - "repository": { - "default": "authup/authup", - "description": "Authup image repository (one image serves both services)", - "required": [], - "title": "repository", - "type": "string" - }, - "tag": { - "default": "", - "description": "Authup image tag (defaults to the chart appVersion)", - "required": [], - "title": "tag", - "type": "string" - } - }, - "required": [ - "registry", - "repository", - "tag", - "digest", - "pullPolicy", - "pullSecrets" - ], - "title": "image", - "type": "object" - }, - "mysql": { - "additionalProperties": false, - "description": "# Built-in single-instance MySQL (docker-official image). Same caveats as postgresql.", - "properties": { - "affinity": { - "additionalProperties": true, - "description": "MySQL affinity", - "required": [], - "title": "affinity" }, - "auth": { + "readinessProbe": { "additionalProperties": false, "properties": { - "database": { - "default": "authup", - "description": "MySQL database name (created on first boot)", + "enabled": { + "default": true, + "description": "Enable the readiness probe", "required": [], - "title": "database", - "type": "string" + "title": "enabled", + "type": "boolean" }, - "password": { - "default": "", - "description": "MySQL password (\"\" = generate once, keep across upgrades)", + "failureThreshold": { + "default": 3, "required": [], - "title": "password", - "type": "string" + "title": "failureThreshold", + "type": "integer" }, - "username": { - "default": "authup", - "description": "MySQL application username", + "initialDelaySeconds": { + "default": 0, "required": [], - "title": "username", - "type": "string" + "title": "initialDelaySeconds", + "type": "integer" + }, + "periodSeconds": { + "default": 10, + "required": [], + "title": "periodSeconds", + "type": "integer" + }, + "successThreshold": { + "default": 1, + "required": [], + "title": "successThreshold", + "type": "integer" + }, + "timeoutSeconds": { + "default": 5, + "required": [], + "title": "timeoutSeconds", + "type": "integer" } }, "required": [ - "username", - "password", - "database" + "enabled", + "initialDelaySeconds", + "periodSeconds", + "timeoutSeconds", + "failureThreshold", + "successThreshold" ], - "title": "auth", + "title": "readinessProbe", "type": "object" }, - "containerSecurityContext": { + "replicaCount": { + "default": 1, + "description": "Number of UI replicas (fully stateless, scale freely)", + "required": [], + "title": "replicaCount", + "type": "integer" + }, + "resources": { "additionalProperties": true, - "description": "MySQL container security context", + "description": "UI container resources", "properties": { - "allowPrivilegeEscalation": { - "default": false, - "required": [], - "title": "allowPrivilegeEscalation", - "type": "boolean" - }, - "capabilities": { - "additionalProperties": false, + "limits": { + "additionalProperties": true, "properties": { - "drop": { - "items": { - "anyOf": [ - { - "required": [], - "type": "string" - } - ], - "required": [] - }, + "memory": { + "default": "512Mi", "required": [], - "title": "drop", - "type": "array" + "title": "memory", + "type": "string" } }, "required": [ - "drop" + "memory" ], - "title": "capabilities", - "type": "object" - }, - "enabled": { - "default": true, - "required": [], - "title": "enabled", - "type": "boolean" - }, - "runAsGroup": { - "default": 999, - "required": [], - "title": "runAsGroup", - "type": "integer" - }, - "runAsNonRoot": { - "default": true, - "required": [], - "title": "runAsNonRoot", - "type": "boolean" - }, - "runAsUser": { - "default": 999, - "required": [], - "title": "runAsUser", - "type": "integer" + "title": "limits" }, - "seccompProfile": { - "additionalProperties": false, + "requests": { + "additionalProperties": true, "properties": { - "type": { - "default": "RuntimeDefault", + "cpu": { + "default": "100m", "required": [], - "title": "type", + "title": "cpu", "type": "string" - } + }, + "memory": { + "default": "256Mi", + "required": [], + "title": "memory", + "type": "string" + } }, "required": [ - "type" + "cpu", + "memory" ], - "title": "seccompProfile", - "type": "object" + "title": "requests" } }, - "required": [ - "enabled", - "runAsUser", - "runAsGroup", - "runAsNonRoot", - "allowPrivilegeEscalation", - "capabilities", - "seccompProfile" - ], - "title": "containerSecurityContext" - }, - "enabled": { - "default": false, - "description": "Deploy the built-in MySQL instance", "required": [], - "title": "enabled", - "type": "boolean" + "title": "resources" }, - "extraEnvVars": { - "description": "Extra environment variables for the MySQL container", - "items": { - "required": [] - }, + "revisionHistoryLimit": { + "default": 3, + "description": "Deployment revision history limit", "required": [], - "title": "extraEnvVars", - "type": "array" + "title": "revisionHistoryLimit", + "type": "integer" }, - "image": { + "route": { "additionalProperties": false, "properties": { - "pullPolicy": { - "default": "IfNotPresent", - "description": "MySQL image pull policy", + "annotations": { + "additionalProperties": true, + "description": "HTTPRoute annotations", "required": [], - "title": "pullPolicy", - "type": "string" + "title": "annotations" }, - "registry": { - "default": "docker.io", - "description": "MySQL image registry", + "enabled": { + "default": false, + "description": "Create a Gateway API HTTPRoute for the UI", "required": [], - "title": "registry", - "type": "string" + "title": "enabled", + "type": "boolean" }, - "repository": { - "default": "mysql", - "description": "MySQL image repository (docker official image)", + "hostnames": { + "description": "Route hostnames ([] = derived from adminConsole.publicUrl / ingress hostname)", + "items": { + "required": [] + }, "required": [], - "title": "repository", - "type": "string" + "title": "hostnames", + "type": "array" }, - "tag": { - "default": "8.4", - "description": "MySQL image tag", + "parentRefs": { + "description": "Gateway parentRefs", + "items": { + "required": [] + }, "required": [], - "title": "tag", - "type": "string" + "title": "parentRefs", + "type": "array" } }, "required": [ - "registry", - "repository", - "tag", - "pullPolicy" + "enabled", + "hostnames", + "parentRefs" ], - "title": "image", + "title": "route", "type": "object" }, - "nodeSelector": { - "additionalProperties": true, - "description": "MySQL node selector", + "schedulerName": { + "default": "", + "description": "Scheduler name", "required": [], - "title": "nodeSelector" + "title": "schedulerName", + "type": "string" }, - "persistence": { + "service": { "additionalProperties": false, "properties": { - "enabled": { - "default": true, - "description": "Persist MySQL data", + "annotations": { + "additionalProperties": true, + "description": "Service annotations (tpl-rendered)", "required": [], - "title": "enabled", - "type": "boolean" + "title": "annotations" }, - "existingClaim": { + "clusterIP": { "default": "", - "description": "Use an existing PVC instead of creating one", + "description": "Static cluster IP", "required": [], - "title": "existingClaim", + "title": "clusterIP", "type": "string" }, - "size": { - "default": "8Gi", - "description": "PVC size", + "externalTrafficPolicy": { + "default": "Cluster", + "description": "External traffic policy", "required": [], - "title": "size", + "title": "externalTrafficPolicy", "type": "string" }, - "storageClass": { + "extraPorts": { + "description": "Extra service ports", + "items": { + "required": [] + }, + "required": [], + "title": "extraPorts", + "type": "array" + }, + "loadBalancerIP": { "default": "", - "description": "PVC storage class (\"\" = cluster default; global.defaultStorageClass wins)", + "description": "LoadBalancer IP", "required": [], - "title": "storageClass", + "title": "loadBalancerIP", "type": "string" - } - }, - "required": [ - "enabled", - "size", - "storageClass", - "existingClaim" - ], - "title": "persistence", - "type": "object" - }, - "podSecurityContext": { - "additionalProperties": true, - "description": "MySQL pod security context", - "properties": { - "enabled": { - "default": true, - "required": [], - "title": "enabled", - "type": "boolean" }, - "fsGroup": { - "default": 999, + "loadBalancerSourceRanges": { + "description": "LoadBalancer source ranges", + "items": { + "required": [] + }, "required": [], - "title": "fsGroup", - "type": "integer" - } - }, - "required": [ - "enabled", - "fsGroup" - ], - "title": "podSecurityContext" - }, - "resources": { - "additionalProperties": true, - "description": "MySQL container resources", - "properties": { - "limits": { - "additionalProperties": true, + "title": "loadBalancerSourceRanges", + "type": "array" + }, + "nodePorts": { + "additionalProperties": false, "properties": { - "memory": { - "default": "1Gi", + "http": { + "default": "", + "description": "Node port (\"\" = auto-assign)", "required": [], - "title": "memory", + "title": "http", "type": "string" } }, "required": [ - "memory" + "http" ], - "title": "limits" + "title": "nodePorts", + "type": "object" }, - "requests": { - "additionalProperties": true, + "ports": { + "additionalProperties": false, "properties": { - "cpu": { - "default": "100m", - "required": [], - "title": "cpu", - "type": "string" - }, - "memory": { - "default": "512Mi", + "http": { + "default": 3000, + "description": "Service HTTP port (the container port is fixed at 3000)", "required": [], - "title": "memory", - "type": "string" + "title": "http", + "type": "integer" } }, "required": [ - "cpu", - "memory" + "http" ], - "title": "requests" + "title": "ports", + "type": "object" + }, + "sessionAffinity": { + "default": "None", + "description": "Session affinity", + "required": [], + "title": "sessionAffinity", + "type": "string" + }, + "sessionAffinityConfig": { + "additionalProperties": true, + "description": "Session affinity config", + "required": [], + "title": "sessionAffinityConfig" + }, + "type": { + "default": "ClusterIP", + "description": "Service type", + "required": [], + "title": "type", + "type": "string" } }, - "required": [], - "title": "resources" - }, - "tolerations": { - "description": "MySQL tolerations", - "items": { - "required": [] + "required": [ + "type", + "ports", + "nodePorts", + "clusterIP", + "loadBalancerIP", + "loadBalancerSourceRanges", + "externalTrafficPolicy", + "sessionAffinity", + "extraPorts" + ], + "title": "service", + "type": "object" + }, + "sidecars": { + "description": "Sidecar containers (tpl-rendered)", + "items": { + "required": [] }, "required": [], - "title": "tolerations", + "title": "sidecars", "type": "array" - } - }, - "required": [ - "enabled", - "image", - "auth", - "persistence", - "tolerations", - "extraEnvVars" - ], - "title": "mysql", - "type": "object" - }, - "nameOverride": { - "default": "", - "description": "Override the chart name", - "required": [], - "title": "nameOverride", - "type": "string" - }, - "namespaceOverride": { - "default": "", - "description": "Override the release namespace", - "required": [], - "title": "namespaceOverride", - "type": "string" - }, - "postgresql": { - "additionalProperties": false, - "description": "# Built-in single-instance PostgreSQL (docker-official image).\n# Convenience for dev / small production; for serious deployments bring an\n# external database or an operator such as CloudNativePG.", - "properties": { - "affinity": { - "additionalProperties": true, - "description": "PostgreSQL affinity", - "required": [], - "title": "affinity" }, - "auth": { + "startupProbe": { "additionalProperties": false, "properties": { - "database": { - "default": "authup", - "description": "PostgreSQL database name (created on first boot)", - "required": [], - "title": "database", - "type": "string" - }, - "password": { - "default": "", - "description": "PostgreSQL password (\"\" = generate once, keep across upgrades)", - "required": [], - "title": "password", - "type": "string" - }, - "username": { - "default": "authup", - "description": "PostgreSQL application username", - "required": [], - "title": "username", - "type": "string" - } - }, - "required": [ - "username", - "password", - "database" - ], - "title": "auth", - "type": "object" - }, - "containerSecurityContext": { - "additionalProperties": true, - "description": "PostgreSQL container security context", - "properties": { - "allowPrivilegeEscalation": { - "default": false, - "required": [], - "title": "allowPrivilegeEscalation", - "type": "boolean" - }, - "capabilities": { - "additionalProperties": false, - "properties": { - "drop": { - "items": { - "anyOf": [ - { - "required": [], - "type": "string" - } - ], - "required": [] - }, - "required": [], - "title": "drop", - "type": "array" - } - }, - "required": [ - "drop" - ], - "title": "capabilities", - "type": "object" - }, "enabled": { "default": true, + "description": "Enable the startup probe", "required": [], "title": "enabled", "type": "boolean" }, - "runAsGroup": { - "default": 999, + "failureThreshold": { + "default": 24, "required": [], - "title": "runAsGroup", + "title": "failureThreshold", "type": "integer" }, - "runAsNonRoot": { - "default": true, + "initialDelaySeconds": { + "default": 5, "required": [], - "title": "runAsNonRoot", - "type": "boolean" + "title": "initialDelaySeconds", + "type": "integer" }, - "runAsUser": { - "default": 999, + "periodSeconds": { + "default": 5, "required": [], - "title": "runAsUser", + "title": "periodSeconds", "type": "integer" }, - "seccompProfile": { - "additionalProperties": false, - "properties": { - "type": { - "default": "RuntimeDefault", - "required": [], - "title": "type", - "type": "string" - } - }, - "required": [ - "type" - ], - "title": "seccompProfile", - "type": "object" + "successThreshold": { + "default": 1, + "required": [], + "title": "successThreshold", + "type": "integer" + }, + "timeoutSeconds": { + "default": 5, + "required": [], + "title": "timeoutSeconds", + "type": "integer" } }, "required": [ "enabled", - "runAsUser", - "runAsGroup", - "runAsNonRoot", - "allowPrivilegeEscalation", - "capabilities", - "seccompProfile" + "initialDelaySeconds", + "periodSeconds", + "timeoutSeconds", + "failureThreshold", + "successThreshold" ], - "title": "containerSecurityContext" + "title": "startupProbe", + "type": "object" }, - "enabled": { - "default": true, - "description": "Deploy the built-in PostgreSQL instance", + "terminationGracePeriodSeconds": { + "default": 30, + "description": "Pod termination grace period", "required": [], - "title": "enabled", - "type": "boolean" + "title": "terminationGracePeriodSeconds", + "type": "integer" }, - "extraEnvVars": { - "description": "Extra environment variables for the PostgreSQL container", + "tolerations": { + "description": "Tolerations", "items": { "required": [] }, "required": [], - "title": "extraEnvVars", + "title": "tolerations", "type": "array" }, - "image": { - "additionalProperties": false, + "topologySpreadConstraints": { + "description": "Topology spread constraints", + "items": { + "required": [] + }, + "required": [], + "title": "topologySpreadConstraints", + "type": "array" + }, + "updateStrategy": { + "additionalProperties": true, + "description": "Deployment update strategy", "properties": { - "pullPolicy": { - "default": "IfNotPresent", - "description": "PostgreSQL image pull policy", - "required": [], - "title": "pullPolicy", - "type": "string" - }, - "registry": { - "default": "docker.io", - "description": "PostgreSQL image registry", - "required": [], - "title": "registry", - "type": "string" - }, - "repository": { - "default": "postgres", - "description": "PostgreSQL image repository (docker official image)", - "required": [], - "title": "repository", - "type": "string" - }, - "tag": { - "default": "17", - "description": "PostgreSQL image tag", + "type": { + "default": "RollingUpdate", "required": [], - "title": "tag", + "title": "type", "type": "string" } }, "required": [ - "registry", - "repository", - "tag", - "pullPolicy" + "type" ], - "title": "image", - "type": "object" - }, - "nodeSelector": { - "additionalProperties": true, - "description": "PostgreSQL node selector", - "required": [], - "title": "nodeSelector" - }, - "persistence": { + "title": "updateStrategy" + } + }, + "required": [ + "enabled", + "replicaCount", + "publicUrl", + "apiUrl", + "internalApiUrl", + "command", + "args", + "extraEnvVars", + "extraEnvVarsCM", + "extraEnvVarsSecret", + "extraVolumes", + "extraVolumeMounts", + "initContainers", + "sidecars", + "hostAliases", + "terminationGracePeriodSeconds", + "revisionHistoryLimit", + "disableRestartOnChanges", + "startupProbe", + "livenessProbe", + "readinessProbe", + "tolerations", + "podAntiAffinityPreset", + "topologySpreadConstraints", + "priorityClassName", + "schedulerName", + "service", + "ingress", + "route", + "pdb", + "autoscaling", + "networkPolicy" + ], + "title": "adminConsole", + "type": "object" + }, + "auth": { + "additionalProperties": false, + "description": "#\n# Credentials seeded into authup at boot. Empty passwords are generated once and\n# kept stable across upgrades via lookup.\n# GITOPS WARNING: lookup is inert under `helm template` and ArgoCD-style\n# renders — every sync would then apply a NEW random value, roll the pods via\n# the checksum annotation, and desync the Secret from the live credential\n# (authup only applies the admin password at first provisioning unless the\n# reset flag is set). GitOps users MUST set explicit values or auth.existingSecret.", + "properties": { + "adminPassword": { + "default": "", + "description": "Initial admin user password (\"\" = generate once, keep across upgrades).\nChanging it after the first install only takes effect with\nadminPasswordReset=true for one upgrade cycle", + "required": [], + "title": "adminPassword", + "type": "string" + }, + "adminPasswordReset": { + "default": false, + "description": "Re-assert the admin password on every boot (USER_ADMIN_PASSWORD_RESET)", + "required": [], + "title": "adminPasswordReset", + "type": "boolean" + }, + "existingSecret": { + "default": "", + "description": "Existing secret holding the keys below instead of the chart-managed secret (tpl-rendered)", + "required": [], + "title": "existingSecret", + "type": "string" + }, + "secretKeys": { "additionalProperties": false, "properties": { - "enabled": { - "default": true, - "description": "Persist PostgreSQL data", - "required": [], - "title": "enabled", - "type": "boolean" - }, - "existingClaim": { - "default": "", - "description": "Use an existing PVC instead of creating one", + "adminPasswordKey": { + "default": "admin-password", + "description": "Key inside the (existing) secret holding the admin password", "required": [], - "title": "existingClaim", + "title": "adminPasswordKey", "type": "string" }, - "size": { - "default": "8Gi", - "description": "PVC size", + "secretsEncryptionKeyKey": { + "default": "secrets-encryption-key", + "description": "Key inside the (existing) secret holding the secrets encryption key", "required": [], - "title": "size", + "title": "secretsEncryptionKeyKey", "type": "string" }, - "storageClass": { - "default": "", - "description": "PVC storage class (\"\" = cluster default; global.defaultStorageClass wins)", + "systemClientSecretKey": { + "default": "system-client-secret", + "description": "Key inside the (existing) secret holding the system client secret", "required": [], - "title": "storageClass", + "title": "systemClientSecretKey", "type": "string" } }, "required": [ - "enabled", - "size", - "storageClass", - "existingClaim" + "adminPasswordKey", + "systemClientSecretKey", + "secretsEncryptionKeyKey" ], - "title": "persistence", + "title": "secretKeys", "type": "object" }, - "podSecurityContext": { - "additionalProperties": true, - "description": "PostgreSQL pod security context", - "properties": { - "enabled": { - "default": true, - "required": [], - "title": "enabled", - "type": "boolean" - }, - "fsGroup": { - "default": 999, - "required": [], - "title": "fsGroup", - "type": "integer" - } - }, - "required": [ - "enabled", - "fsGroup" - ], - "title": "podSecurityContext" + "secretsEncryptionKey": { + "default": "", + "description": "Optional AES-256 key-encryption-key wrapping the realm key store\n(SECRETS_ENCRYPTION_KEY, base64-encoded 32 bytes). Deliberately never\ngenerated by the chart: this key is effectively write-once — losing or\nrotating it bricks wrapped MFA seeds and signing keys. Set it explicitly\nhere, or reference it via existingSecret plus\nsecretsEncryptionKeyEnabled=true, and back it up.", + "required": [], + "title": "secretsEncryptionKey", + "type": "string" }, - "resources": { - "additionalProperties": true, - "description": "PostgreSQL container resources", - "properties": { - "limits": { - "additionalProperties": true, - "properties": { - "memory": { - "default": "1Gi", - "required": [], - "title": "memory", - "type": "string" - } - }, - "required": [ - "memory" - ], - "title": "limits" - }, - "requests": { - "additionalProperties": true, - "properties": { - "cpu": { - "default": "100m", - "required": [], - "title": "cpu", - "type": "string" - }, - "memory": { - "default": "256Mi", - "required": [], - "title": "memory", - "type": "string" - } - }, - "required": [ - "cpu", - "memory" - ], - "title": "requests" - } - }, + "secretsEncryptionKeyEnabled": { + "default": false, + "description": "Declare that auth.existingSecret carries the secrets encryption key\n(under secretKeys.secretsEncryptionKeyKey). Required for the key to be\nwired from an existing secret — it is never inferred, so a missing key can\nnot silently fail open into plaintext-at-rest", "required": [], - "title": "resources" + "title": "secretsEncryptionKeyEnabled", + "type": "boolean" }, - "tolerations": { - "description": "PostgreSQL tolerations", - "items": { - "required": [] - }, + "systemClientEnabled": { + "default": false, + "description": "Provision the built-in system client (CLIENT_SYSTEM_ENABLED); required for machine-to-machine consumers", "required": [], - "title": "tolerations", - "type": "array" + "title": "systemClientEnabled", + "type": "boolean" + }, + "systemClientSecret": { + "default": "", + "description": "System client secret (\"\" = generate once when systemClientEnabled)", + "required": [], + "title": "systemClientSecret", + "type": "string" + }, + "systemClientSecretReset": { + "default": false, + "description": "Re-assert the system client secret on every boot (CLIENT_SYSTEM_SECRET_RESET)", + "required": [], + "title": "systemClientSecretReset", + "type": "boolean" } }, "required": [ - "enabled", - "image", - "auth", - "persistence", - "tolerations", - "extraEnvVars" + "adminPassword", + "adminPasswordReset", + "systemClientEnabled", + "systemClientSecret", + "systemClientSecretReset", + "secretsEncryptionKey", + "secretsEncryptionKeyEnabled", + "existingSecret", + "secretKeys" ], - "title": "postgresql", + "title": "auth", "type": "object" }, - "server": { + "commonAnnotations": { + "additionalProperties": true, + "description": "Annotations added to every object", + "required": [], + "title": "commonAnnotations" + }, + "commonLabels": { + "additionalProperties": true, + "description": "Labels added to every object", + "required": [], + "title": "commonLabels" + }, + "database": { "additionalProperties": false, - "description": "#", + "description": "#\n# Exactly one of: built-in postgresql, built-in mysql, or externalDatabase.", "properties": { - "affinity": { - "additionalProperties": true, - "description": "Affinity (overrides the anti-affinity preset when set)", - "required": [], - "title": "affinity" - }, - "args": { - "description": "Override the container args", - "items": { - "required": [] - }, + "type": { + "default": "postgres", + "description": "Database engine when using externalDatabase: postgres or mysql", "required": [], - "title": "args", - "type": "array" - }, - "autoscaling": { - "additionalProperties": false, - "properties": { - "hpa": { - "additionalProperties": false, - "properties": { - "enabled": { - "default": false, - "description": "Enable HPA for server-core (requires a configured cache)", - "required": [], - "title": "enabled", - "type": "boolean" - }, - "maxReplicas": { - "default": 5, - "description": "Maximum replicas", - "required": [], - "title": "maxReplicas", - "type": "integer" - }, - "minReplicas": { - "default": 2, - "description": "Minimum replicas", - "required": [], - "title": "minReplicas", - "type": "integer" - }, - "targetCPU": { - "default": 75, - "description": "Target CPU utilization percentage", - "required": [], - "title": "targetCPU", - "type": "integer" - }, - "targetMemory": { - "default": "", - "description": "Target memory utilization percentage", - "required": [], - "title": "targetMemory", - "type": "string" - } - }, - "required": [ - "enabled", - "minReplicas", - "maxReplicas", - "targetCPU", - "targetMemory" - ], - "title": "hpa", - "type": "object" - } + "title": "type", + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "database", + "type": "object" + }, + "diagnosticMode": { + "additionalProperties": false, + "properties": { + "args": { + "description": "Args overriding the containers' args in diagnostic mode", + "items": { + "anyOf": [ + { + "required": [], + "type": "string" + } + ], + "required": [] }, - "required": [ - "hpa" - ], - "title": "autoscaling", - "type": "object" + "required": [], + "title": "args", + "type": "array" }, "command": { - "description": "Override the container command", + "description": "Command overriding the containers' command in diagnostic mode", "items": { + "anyOf": [ + { + "required": [], + "type": "string" + } + ], "required": [] }, "required": [], "title": "command", "type": "array" }, - "config": { - "additionalProperties": true, - "description": "Extra environment variables rendered literally into the env ConfigMap\n(map of NAME: value) for options without first-class values", + "enabled": { + "default": false, + "description": "Start every container with a sleep command and disable probes (debugging)", "required": [], - "title": "config" + "title": "enabled", + "type": "boolean" + } + }, + "required": [ + "enabled", + "command", + "args" + ], + "title": "diagnosticMode", + "type": "object" + }, + "externalDatabase": { + "additionalProperties": false, + "properties": { + "database": { + "default": "authup", + "description": "External database name (must exist, or the user needs CREATE privilege)", + "required": [], + "title": "database", + "type": "string" }, - "configuration": { + "existingSecret": { "default": "", - "description": "Content of an authup.server.core.conf mounted into the working directory\nfor file-only options (middleware objects, per-field SMTP, CORS allowlist).\nEnvironment variables always win over file values.", + "description": "Existing secret with the database password (tpl-rendered)", "required": [], - "title": "configuration", + "title": "existingSecret", "type": "string" }, - "containerSecurityContext": { - "additionalProperties": true, - "description": "Container security context. The upstream image runs as root and needs a\nwritable npm cache; the chart mounts emptyDirs at /usr/src/app/writable and\n/tmp to keep readOnlyRootFilesystem viable.", - "properties": { - "allowPrivilegeEscalation": { - "default": false, - "required": [], - "title": "allowPrivilegeEscalation", - "type": "boolean" - }, - "capabilities": { - "additionalProperties": false, - "properties": { - "drop": { - "items": { - "anyOf": [ - { - "required": [], - "type": "string" - } - ], - "required": [] - }, - "required": [], - "title": "drop", - "type": "array" - } - }, - "required": [ - "drop" - ], - "title": "capabilities", - "type": "object" - }, - "enabled": { - "default": true, - "required": [], - "title": "enabled", - "type": "boolean" - }, - "readOnlyRootFilesystem": { - "default": false, - "required": [], - "title": "readOnlyRootFilesystem", - "type": "boolean" - }, - "runAsNonRoot": { - "default": false, - "required": [], - "title": "runAsNonRoot", - "type": "boolean" - }, - "runAsUser": { - "default": 0, - "required": [], - "title": "runAsUser", - "type": "integer" - }, - "seccompProfile": { - "additionalProperties": false, - "properties": { - "type": { - "default": "RuntimeDefault", - "required": [], - "title": "type", - "type": "string" - } - }, - "required": [ - "type" - ], - "title": "seccompProfile", - "type": "object" - } - }, - "required": [ - "enabled", - "runAsUser", - "runAsNonRoot", - "allowPrivilegeEscalation", - "capabilities", - "seccompProfile", - "readOnlyRootFilesystem" - ], - "title": "containerSecurityContext" - }, - "customLivenessProbe": { - "additionalProperties": true, - "description": "Custom liveness probe", + "existingSecretPasswordKey": { + "default": "password", + "description": "Key inside externalDatabase.existingSecret holding the password", "required": [], - "title": "customLivenessProbe" + "title": "existingSecretPasswordKey", + "type": "string" }, - "customReadinessProbe": { - "additionalProperties": true, - "description": "Custom readiness probe", + "host": { + "default": "", + "description": "External database host (tpl-rendered). Setting this selects the external database", "required": [], - "title": "customReadinessProbe" + "title": "host", + "type": "string" }, - "customStartupProbe": { - "additionalProperties": true, - "description": "Custom startup probe overriding the structured one", + "password": { + "default": "", + "description": "External database password (stored in a chart-managed secret, never inline env)", "required": [], - "title": "customStartupProbe" + "title": "password", + "type": "string" }, - "disableRestartOnChanges": { - "default": false, - "description": "Disable the checksum annotations that roll pods on config/secret changes", + "port": { + "default": "", + "description": "External database port (\"\" = engine default: 5432 / 3306)", "required": [], - "title": "disableRestartOnChanges", - "type": "boolean" + "title": "port", + "type": "string" }, - "enabled": { - "default": true, - "description": "Deploy the server-core service", + "user": { + "default": "authup", + "description": "External database user", "required": [], - "title": "enabled", - "type": "boolean" - }, - "existingConfigmap": { + "title": "user", + "type": "string" + } + }, + "required": [ + "host", + "port", + "user", + "database", + "password", + "existingSecret", + "existingSecretPasswordKey" + ], + "title": "externalDatabase", + "type": "object" + }, + "externalRedis": { + "additionalProperties": false, + "properties": { + "existingSecret": { "default": "", - "description": "Existing ConfigMap holding authup.server.core.conf (tpl-rendered)", + "description": "Existing secret holding a FULL connection URL (tpl-rendered)", "required": [], - "title": "existingConfigmap", + "title": "existingSecret", "type": "string" }, - "extraEnvVars": { - "description": "Extra environment variables for the server container", - "items": { - "required": [] - }, + "existingSecretKey": { + "default": "redis-connection-string", + "description": "Key inside externalRedis.existingSecret holding the connection URL", "required": [], - "title": "extraEnvVars", - "type": "array" + "title": "existingSecretKey", + "type": "string" }, - "extraEnvVarsCM": { + "host": { "default": "", - "description": "Extra ConfigMap with environment variables (tpl-rendered name)", + "description": "External Redis host (alternative to url; the chart composes the URL)", "required": [], - "title": "extraEnvVarsCM", + "title": "host", "type": "string" }, - "extraEnvVarsSecret": { + "password": { "default": "", - "description": "Extra Secret with environment variables (tpl-rendered name)", + "description": "External Redis password", "required": [], - "title": "extraEnvVarsSecret", + "title": "password", "type": "string" }, - "extraVolumeMounts": { - "description": "Extra volume mounts (tpl-rendered)", - "items": { - "required": [] - }, + "port": { + "default": 6379, + "description": "External Redis port", "required": [], - "title": "extraVolumeMounts", - "type": "array" + "title": "port", + "type": "integer" }, - "extraVolumes": { - "description": "Extra volumes (tpl-rendered)", - "items": { - "required": [] - }, + "url": { + "default": "", + "description": "Full external Redis/Valkey connection URL (redis://[user:pass@]host:port); stored in a chart-managed secret", "required": [], - "title": "extraVolumes", - "type": "array" - }, - "features": { - "additionalProperties": false, - "properties": { - "emailVerification": { - "default": false, - "description": "Enable email verification (EMAIL_VERIFICATION_ENABLED; requires SMTP)", - "required": [], - "title": "emailVerification", - "type": "boolean" - }, - "passwordRecovery": { - "default": false, - "description": "Enable password recovery (PASSWORD_RECOVERY_ENABLED; requires SMTP)", - "required": [], - "title": "passwordRecovery", - "type": "boolean" - }, - "registration": { - "default": false, - "description": "Enable self-service user registration (REGISTRATION_ENABLED)", - "required": [], - "title": "registration", - "type": "boolean" - } - }, - "required": [ - "registration", - "passwordRecovery", - "emailVerification" - ], - "title": "features", - "type": "object" - }, - "hostAliases": { - "description": "Pod host aliases", - "items": { - "required": [] - }, + "title": "url", + "type": "string" + } + }, + "required": [ + "url", + "host", + "port", + "password", + "existingSecret", + "existingSecretKey" + ], + "title": "externalRedis", + "type": "object" + }, + "extraDeploy": { + "description": "Extra objects to deploy (rendered through tpl; list of manifests or strings)", + "items": { + "required": [] + }, + "required": [], + "title": "extraDeploy", + "type": "array" + }, + "fullnameOverride": { + "default": "", + "description": "Override the fully qualified release name", + "required": [], + "title": "fullnameOverride", + "type": "string" + }, + "global": { + "additionalProperties": false, + "properties": { + "defaultStorageClass": { + "default": "", + "description": "Global default storage class for dynamic provisioning", "required": [], - "title": "hostAliases", - "type": "array" - }, - "ingress": { - "additionalProperties": false, - "properties": { - "annotations": { - "additionalProperties": true, - "description": "Ingress annotations (tpl-rendered). Token responses are large; with\ningress-nginx consider proxy-buffer-size 16k+.", - "required": [], - "title": "annotations" - }, - "certManager": { - "default": false, - "description": "Request a cert-manager certificate (adds kubernetes.io/tls-acme)", - "required": [], - "title": "certManager", - "type": "boolean" - }, - "enabled": { - "default": false, - "description": "Enable ingress for server-core. NOTE: this also exposes the\nUNAUTHENTICATED /metrics endpoint publicly — block it at the ingress\ncontroller or disable it via server.configuration\n(\"middlewarePrometheus: false\") when it is not scraped", - "required": [], - "title": "enabled", - "type": "boolean" - }, - "extraHosts": { - "description": "Extra hosts", - "items": { - "required": [] - }, - "required": [], - "title": "extraHosts", - "type": "array" - }, - "extraPaths": { - "description": "Extra paths for the primary host", - "items": { - "required": [] - }, - "required": [], - "title": "extraPaths", - "type": "array" - }, - "extraRules": { - "description": "Full custom rules (tpl-rendered; overrides the generated rule)", - "items": { - "required": [] - }, - "required": [], - "title": "extraRules", - "type": "array" - }, - "extraTls": { - "description": "Extra TLS entries", - "items": { - "required": [] - }, - "required": [], - "title": "extraTls", - "type": "array" - }, - "hostname": { - "default": "", - "description": "Ingress hostname (tpl-rendered); also drives the derived PUBLIC_URL", - "required": [], - "title": "hostname", - "type": "string" - }, - "ingressClassName": { - "default": "", - "description": "Ingress class name", - "required": [], - "title": "ingressClassName", - "type": "string" - }, - "path": { - "default": "/", - "description": "Ingress path", - "required": [], - "title": "path", - "type": "string" - }, - "pathType": { - "default": "Prefix", - "description": "Ingress path type", - "required": [], - "title": "pathType", - "type": "string" - }, - "tls": { - "default": false, - "description": "Enable TLS for the hostname (secret \u003chostname\u003e-tls unless extraTls overrides)", - "required": [], - "title": "tls", - "type": "boolean" - } - }, - "required": [ - "enabled", - "hostname", - "path", - "pathType", - "ingressClassName", - "tls", - "certManager", - "extraHosts", - "extraPaths", - "extraTls", - "extraRules" - ], - "title": "ingress", - "type": "object" + "title": "defaultStorageClass", + "type": "string" }, - "initContainers": { - "description": "Init containers (tpl-rendered)", + "imagePullSecrets": { + "description": "Global image pull secrets (list of names or objects)", "items": { "required": [] }, "required": [], - "title": "initContainers", + "title": "imagePullSecrets", "type": "array" }, - "lifecycleHooks": { - "additionalProperties": true, - "description": "Container lifecycle hooks", + "imageRegistry": { + "default": "", + "description": "Global container image registry override (takes precedence over image.registry)", "required": [], - "title": "lifecycleHooks" - }, - "livenessProbe": { - "additionalProperties": false, - "properties": { - "enabled": { - "default": true, - "description": "Enable the liveness probe (GET / status endpoint)", - "required": [], - "title": "enabled", - "type": "boolean" - }, - "failureThreshold": { - "default": 3, - "required": [], - "title": "failureThreshold", - "type": "integer" - }, - "initialDelaySeconds": { - "default": 0, - "required": [], - "title": "initialDelaySeconds", - "type": "integer" - }, - "periodSeconds": { - "default": 30, - "required": [], - "title": "periodSeconds", - "type": "integer" - }, - "successThreshold": { - "default": 1, - "required": [], - "title": "successThreshold", - "type": "integer" - }, - "timeoutSeconds": { - "default": 5, - "required": [], - "title": "timeoutSeconds", - "type": "integer" - } - }, - "required": [ - "enabled", - "initialDelaySeconds", - "periodSeconds", - "timeoutSeconds", - "failureThreshold", - "successThreshold" - ], - "title": "livenessProbe", - "type": "object" + "title": "imageRegistry", + "type": "string" + } + }, + "required": [ + "imageRegistry", + "imagePullSecrets", + "defaultStorageClass" + ], + "title": "global", + "type": "object" + }, + "image": { + "additionalProperties": false, + "description": "#", + "properties": { + "digest": { + "default": "", + "description": "Authup image digest (takes precedence over tag)", + "required": [], + "title": "digest", + "type": "string" }, - "metrics": { - "additionalProperties": false, - "properties": { - "serviceMonitor": { - "additionalProperties": false, - "description": "The /metrics endpoint is UNAUTHENTICATED; keep it off the public ingress", - "properties": { - "enabled": { - "default": false, - "description": "Create a prometheus-operator ServiceMonitor scraping /metrics", - "required": [], - "title": "enabled", - "type": "boolean" - }, - "honorLabels": { - "default": false, - "description": "Honor labels", - "required": [], - "title": "honorLabels", - "type": "boolean" - }, - "interval": { - "default": "30s", - "description": "Scrape interval", - "required": [], - "title": "interval", - "type": "string" - }, - "jobLabel": { - "default": "", - "description": "Job label", - "required": [], - "title": "jobLabel", - "type": "string" - }, - "labels": { - "additionalProperties": true, - "description": "ServiceMonitor labels", - "required": [], - "title": "labels" - }, - "metricRelabelings": { - "description": "Metric relabelings", - "items": { - "required": [] - }, - "required": [], - "title": "metricRelabelings", - "type": "array" - }, - "namespace": { - "default": "", - "description": "ServiceMonitor namespace (\"\" = release namespace)", - "required": [], - "title": "namespace", - "type": "string" - }, - "relabelings": { - "description": "Relabelings", - "items": { - "required": [] - }, - "required": [], - "title": "relabelings", - "type": "array" - }, - "scrapeTimeout": { - "default": "", - "description": "Scrape timeout", - "required": [], - "title": "scrapeTimeout", - "type": "string" - } - }, - "required": [ - "enabled", - "namespace", - "interval", - "scrapeTimeout", - "honorLabels", - "metricRelabelings", - "relabelings", - "jobLabel" - ], - "title": "serviceMonitor", - "type": "object" - } - }, - "required": [ - "serviceMonitor" - ], - "title": "metrics", - "type": "object" + "pullPolicy": { + "default": "IfNotPresent", + "description": "Authup image pull policy", + "required": [], + "title": "pullPolicy", + "type": "string" }, - "mfa": { - "additionalProperties": false, - "properties": { - "enabled": { - "default": false, - "description": "Enable multi-factor authentication (MFA_ENABLED)", - "required": [], - "title": "enabled", - "type": "boolean" - }, - "required": { - "default": false, - "description": "Require MFA for every user (MFA_REQUIRED; needs mfa.enabled)", - "required": [], - "title": "required", - "type": "boolean" - } + "pullSecrets": { + "description": "Authup image pull secrets", + "items": { + "required": [] }, - "required": [ - "enabled", - "required" - ], - "title": "mfa", - "type": "object" + "required": [], + "title": "pullSecrets", + "type": "array" }, - "migration": { - "additionalProperties": false, - "properties": { - "backoffLimit": { - "default": 3, - "description": "Job backoff limit", - "required": [], - "title": "backoffLimit", - "type": "integer" - }, - "enabled": { - "default": false, - "description": "Run `server/core migration run` as a pre-upgrade hook Job. Recommended\nfor multi-replica deployments (serializes DDL before pods roll). Fresh\ninstalls migrate at boot regardless.", - "required": [], - "title": "enabled", - "type": "boolean" - }, - "podAnnotations": { - "additionalProperties": true, - "description": "Job pod annotations", - "required": [], - "title": "podAnnotations" - }, - "resources": { - "additionalProperties": true, - "description": "Job resources ({} = server resources defaults)", - "required": [], - "title": "resources" - }, - "ttlSecondsAfterFinished": { - "default": 300, - "description": "Delete the Job this many seconds after it finishes (\"\" = keep)", - "required": [], - "title": "ttlSecondsAfterFinished", - "type": "integer" - } - }, - "required": [ - "enabled", - "backoffLimit", - "ttlSecondsAfterFinished" - ], - "title": "migration", - "type": "object" + "registry": { + "default": "docker.io", + "description": "Authup image registry", + "required": [], + "title": "registry", + "type": "string" }, - "networkPolicy": { - "additionalProperties": false, - "properties": { - "allowExternal": { - "default": true, - "description": "Allow ingress from anywhere. When false, only same-namespace pods,\nthe release's UI pods and the configured selectors may connect — add\nyour ingress controller via ingressNSMatchLabels/ingressPodMatchLabels", - "required": [], - "title": "allowExternal", - "type": "boolean" - }, - "allowExternalEgress": { - "default": true, - "description": "Allow all egress", - "required": [], - "title": "allowExternalEgress", - "type": "boolean" - }, - "enabled": { - "default": false, - "description": "Create a NetworkPolicy for server-core", - "required": [], - "title": "enabled", - "type": "boolean" - }, - "extraEgress": { - "description": "Extra egress rules", - "items": { - "required": [] - }, - "required": [], - "title": "extraEgress", - "type": "array" - }, - "extraIngress": { - "description": "Extra ingress rules", - "items": { - "required": [] - }, - "required": [], - "title": "extraIngress", - "type": "array" - }, - "ingressNSMatchLabels": { - "additionalProperties": true, - "description": "Namespace labels allowed to connect when allowExternal is false", - "required": [], - "title": "ingressNSMatchLabels" - }, - "ingressPodMatchLabels": { - "additionalProperties": true, - "description": "Pod labels allowed to connect when allowExternal is false", - "required": [], - "title": "ingressPodMatchLabels" - } - }, - "required": [ - "enabled", - "allowExternal", - "allowExternalEgress", - "extraIngress", - "extraEgress" - ], - "title": "networkPolicy", - "type": "object" + "repository": { + "default": "authup/authup", + "description": "Authup image repository (one image serves both services)", + "required": [], + "title": "repository", + "type": "string" }, - "nodeSelector": { + "tag": { + "default": "", + "description": "Authup image tag (defaults to the chart appVersion)", + "required": [], + "title": "tag", + "type": "string" + } + }, + "required": [ + "registry", + "repository", + "tag", + "digest", + "pullPolicy", + "pullSecrets" + ], + "title": "image", + "type": "object" + }, + "mysql": { + "additionalProperties": false, + "description": "# Built-in single-instance MySQL (docker-official image). Same caveats as postgresql.", + "properties": { + "affinity": { "additionalProperties": true, - "description": "Node selector", + "description": "MySQL affinity", "required": [], - "title": "nodeSelector" + "title": "affinity" }, - "pdb": { + "auth": { "additionalProperties": false, "properties": { - "create": { - "default": false, - "description": "Create a PodDisruptionBudget for server-core", + "database": { + "default": "authup", + "description": "MySQL database name (created on first boot)", "required": [], - "title": "create", - "type": "boolean" + "title": "database", + "type": "string" }, - "maxUnavailable": { + "password": { "default": "", - "description": "Maximum unavailable pods (defaults to 1 when both are empty)", + "description": "MySQL password (\"\" = generate once, keep across upgrades)", "required": [], - "title": "maxUnavailable", + "title": "password", "type": "string" }, - "minAvailable": { - "default": "", - "description": "Minimum available pods", + "username": { + "default": "authup", + "description": "MySQL application username", "required": [], - "title": "minAvailable", + "title": "username", "type": "string" } }, "required": [ - "create", - "minAvailable", - "maxUnavailable" + "username", + "password", + "database" ], - "title": "pdb", + "title": "auth", "type": "object" }, - "podAnnotations": { - "additionalProperties": true, - "description": "Pod annotations (tpl-rendered)", - "required": [], - "title": "podAnnotations" - }, - "podAntiAffinityPreset": { - "default": "soft", - "description": "Pod anti-affinity preset: soft, hard or \"\"", - "required": [], - "title": "podAntiAffinityPreset", - "type": "string" - }, - "podLabels": { - "additionalProperties": true, - "description": "Pod labels (tpl-rendered)", - "required": [], - "title": "podLabels" - }, - "podSecurityContext": { + "containerSecurityContext": { "additionalProperties": true, - "description": "Pod security context", + "description": "MySQL container security context", "properties": { + "allowPrivilegeEscalation": { + "default": false, + "required": [], + "title": "allowPrivilegeEscalation", + "type": "boolean" + }, + "capabilities": { + "additionalProperties": false, + "properties": { + "drop": { + "items": { + "anyOf": [ + { + "required": [], + "type": "string" + } + ], + "required": [] + }, + "required": [], + "title": "drop", + "type": "array" + } + }, + "required": [ + "drop" + ], + "title": "capabilities", + "type": "object" + }, "enabled": { "default": true, "required": [], "title": "enabled", "type": "boolean" }, - "fsGroup": { - "default": 1000, + "runAsGroup": { + "default": 999, "required": [], - "title": "fsGroup", + "title": "runAsGroup", + "type": "integer" + }, + "runAsNonRoot": { + "default": true, + "required": [], + "title": "runAsNonRoot", + "type": "boolean" + }, + "runAsUser": { + "default": 999, + "required": [], + "title": "runAsUser", "type": "integer" + }, + "seccompProfile": { + "additionalProperties": false, + "properties": { + "type": { + "default": "RuntimeDefault", + "required": [], + "title": "type", + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "seccompProfile", + "type": "object" } }, "required": [ "enabled", - "fsGroup" + "runAsUser", + "runAsGroup", + "runAsNonRoot", + "allowPrivilegeEscalation", + "capabilities", + "seccompProfile" ], - "title": "podSecurityContext" + "title": "containerSecurityContext" }, - "priorityClassName": { - "default": "", - "description": "Priority class name", + "enabled": { + "default": false, + "description": "Deploy the built-in MySQL instance", "required": [], - "title": "priorityClassName", - "type": "string" + "title": "enabled", + "type": "boolean" }, - "provisioning": { + "extraEnvVars": { + "description": "Extra environment variables for the MySQL container", + "items": { + "required": [] + }, + "required": [], + "title": "extraEnvVars", + "type": "array" + }, + "image": { "additionalProperties": false, "properties": { - "enabled": { - "default": false, - "description": "Mount provisioning files consumed at boot (fail-closed: an invalid file aborts startup)", + "pullPolicy": { + "default": "IfNotPresent", + "description": "MySQL image pull policy", "required": [], - "title": "enabled", - "type": "boolean" + "title": "pullPolicy", + "type": "string" }, - "existingConfigMap": { - "default": "", - "description": "Existing ConfigMap with provisioning files (tpl-rendered)", + "registry": { + "default": "docker.io", + "description": "MySQL image registry", "required": [], - "title": "existingConfigMap", + "title": "registry", "type": "string" }, - "existingSecret": { - "default": "", - "description": "Existing Secret with provisioning files (tpl-rendered; takes\nprecedence — use for provisioning content that carries credentials)", + "repository": { + "default": "mysql", + "description": "MySQL image repository (docker official image)", "required": [], - "title": "existingSecret", + "title": "repository", "type": "string" }, - "files": { - "additionalProperties": true, - "description": "Map of filename -\u003e file content (tpl-rendered; the extension selects\nthe reader). Lands in a ConfigMap — credential-bearing provisioning\ncontent belongs in existingSecret instead", + "tag": { + "default": "8.4", + "description": "MySQL image tag", "required": [], - "title": "files" + "title": "tag", + "type": "string" } }, "required": [ - "enabled", - "existingConfigMap", - "existingSecret" + "registry", + "repository", + "tag", + "pullPolicy" ], - "title": "provisioning", + "title": "image", "type": "object" }, - "publicUrl": { - "default": "", - "description": "Public URL of server-core (PUBLIC_URL) — the OIDC issuer origin.\n\"\" = derived from server.ingress when enabled. Changing it later breaks\nenrolled WebAuthn credentials and the OIDC issuer.", + "nodeSelector": { + "additionalProperties": true, + "description": "MySQL node selector", "required": [], - "title": "publicUrl", - "type": "string" + "title": "nodeSelector" }, - "readinessProbe": { + "persistence": { "additionalProperties": false, "properties": { "enabled": { "default": true, - "description": "Enable the readiness probe (GET / status endpoint)", + "description": "Persist MySQL data", "required": [], "title": "enabled", "type": "boolean" }, - "failureThreshold": { - "default": 3, + "existingClaim": { + "default": "", + "description": "Use an existing PVC instead of creating one", "required": [], - "title": "failureThreshold", - "type": "integer" + "title": "existingClaim", + "type": "string" }, - "initialDelaySeconds": { - "default": 0, + "size": { + "default": "8Gi", + "description": "PVC size", "required": [], - "title": "initialDelaySeconds", - "type": "integer" + "title": "size", + "type": "string" }, - "periodSeconds": { - "default": 10, + "storageClass": { + "default": "", + "description": "PVC storage class (\"\" = cluster default; global.defaultStorageClass wins)", "required": [], - "title": "periodSeconds", - "type": "integer" - }, - "successThreshold": { - "default": 1, + "title": "storageClass", + "type": "string" + } + }, + "required": [ + "enabled", + "size", + "storageClass", + "existingClaim" + ], + "title": "persistence", + "type": "object" + }, + "podSecurityContext": { + "additionalProperties": true, + "description": "MySQL pod security context", + "properties": { + "enabled": { + "default": true, "required": [], - "title": "successThreshold", - "type": "integer" + "title": "enabled", + "type": "boolean" }, - "timeoutSeconds": { - "default": 5, + "fsGroup": { + "default": 999, "required": [], - "title": "timeoutSeconds", + "title": "fsGroup", "type": "integer" } }, "required": [ "enabled", - "initialDelaySeconds", - "periodSeconds", - "timeoutSeconds", - "failureThreshold", - "successThreshold" + "fsGroup" ], - "title": "readinessProbe", - "type": "object" - }, - "replicaCount": { - "default": 1, - "description": "Number of server-core replicas (values \u003e 1 REQUIRE a configured cache)", - "required": [], - "title": "replicaCount", - "type": "integer" + "title": "podSecurityContext" }, "resources": { "additionalProperties": true, - "description": "Server container resources", + "description": "MySQL container resources", "properties": { "limits": { "additionalProperties": true, "properties": { "memory": { - "default": "2Gi", + "default": "1Gi", "required": [], "title": "memory", "type": "string" @@ -2011,7 +1732,7 @@ "additionalProperties": true, "properties": { "cpu": { - "default": "250m", + "default": "100m", "required": [], "title": "cpu", "type": "string" @@ -2033,426 +1754,364 @@ "required": [], "title": "resources" }, - "revisionHistoryLimit": { - "default": 3, - "description": "Deployment revision history limit", + "tolerations": { + "description": "MySQL tolerations", + "items": { + "required": [] + }, + "required": [], + "title": "tolerations", + "type": "array" + } + }, + "required": [ + "enabled", + "image", + "auth", + "persistence", + "tolerations", + "extraEnvVars" + ], + "title": "mysql", + "type": "object" + }, + "nameOverride": { + "default": "", + "description": "Override the chart name", + "required": [], + "title": "nameOverride", + "type": "string" + }, + "namespaceOverride": { + "default": "", + "description": "Override the release namespace", + "required": [], + "title": "namespaceOverride", + "type": "string" + }, + "postgresql": { + "additionalProperties": false, + "description": "# Built-in single-instance PostgreSQL (docker-official image).\n# Convenience for dev / small production; for serious deployments bring an\n# external database or an operator such as CloudNativePG.", + "properties": { + "affinity": { + "additionalProperties": true, + "description": "PostgreSQL affinity", "required": [], - "title": "revisionHistoryLimit", - "type": "integer" + "title": "affinity" }, - "route": { + "auth": { "additionalProperties": false, "properties": { - "annotations": { - "additionalProperties": true, - "description": "HTTPRoute annotations", - "required": [], - "title": "annotations" - }, - "enabled": { - "default": false, - "description": "Create a Gateway API HTTPRoute for server-core", + "database": { + "default": "authup", + "description": "PostgreSQL database name (created on first boot)", "required": [], - "title": "enabled", - "type": "boolean" + "title": "database", + "type": "string" }, - "hostnames": { - "description": "Route hostnames ([] = derived from server.publicUrl / ingress hostname)", - "items": { - "required": [] - }, + "password": { + "default": "", + "description": "PostgreSQL password (\"\" = generate once, keep across upgrades)", "required": [], - "title": "hostnames", - "type": "array" + "title": "password", + "type": "string" }, - "parentRefs": { - "description": "Gateway parentRefs", - "items": { - "required": [] - }, + "username": { + "default": "authup", + "description": "PostgreSQL application username", "required": [], - "title": "parentRefs", - "type": "array" + "title": "username", + "type": "string" } }, "required": [ - "enabled", - "hostnames", - "parentRefs" + "username", + "password", + "database" ], - "title": "route", + "title": "auth", "type": "object" }, - "schedulerName": { - "default": "", - "description": "Scheduler name", - "required": [], - "title": "schedulerName", - "type": "string" - }, - "service": { - "additionalProperties": false, + "containerSecurityContext": { + "additionalProperties": true, + "description": "PostgreSQL container security context", "properties": { - "annotations": { - "additionalProperties": true, - "description": "Service annotations (tpl-rendered)", + "allowPrivilegeEscalation": { + "default": false, "required": [], - "title": "annotations" + "title": "allowPrivilegeEscalation", + "type": "boolean" }, - "clusterIP": { - "default": "", - "description": "Static cluster IP", - "required": [], - "title": "clusterIP", - "type": "string" + "capabilities": { + "additionalProperties": false, + "properties": { + "drop": { + "items": { + "anyOf": [ + { + "required": [], + "type": "string" + } + ], + "required": [] + }, + "required": [], + "title": "drop", + "type": "array" + } + }, + "required": [ + "drop" + ], + "title": "capabilities", + "type": "object" }, - "externalTrafficPolicy": { - "default": "Cluster", - "description": "External traffic policy", + "enabled": { + "default": true, "required": [], - "title": "externalTrafficPolicy", - "type": "string" + "title": "enabled", + "type": "boolean" }, - "extraPorts": { - "description": "Extra service ports", - "items": { - "required": [] - }, + "runAsGroup": { + "default": 999, "required": [], - "title": "extraPorts", - "type": "array" + "title": "runAsGroup", + "type": "integer" }, - "loadBalancerIP": { - "default": "", - "description": "LoadBalancer IP", + "runAsNonRoot": { + "default": true, "required": [], - "title": "loadBalancerIP", - "type": "string" + "title": "runAsNonRoot", + "type": "boolean" }, - "loadBalancerSourceRanges": { - "description": "LoadBalancer source ranges", - "items": { - "required": [] - }, + "runAsUser": { + "default": 999, "required": [], - "title": "loadBalancerSourceRanges", - "type": "array" + "title": "runAsUser", + "type": "integer" }, - "nodePorts": { + "seccompProfile": { "additionalProperties": false, "properties": { - "http": { - "default": "", - "description": "Node port (\"\" = auto-assign)", + "type": { + "default": "RuntimeDefault", "required": [], - "title": "http", + "title": "type", "type": "string" } }, "required": [ - "http" - ], - "title": "nodePorts", - "type": "object" - }, - "ports": { - "additionalProperties": false, - "properties": { - "http": { - "default": 3000, - "description": "Service HTTP port (the container port is fixed at 3000)", - "required": [], - "title": "http", - "type": "integer" - } - }, - "required": [ - "http" + "type" ], - "title": "ports", + "title": "seccompProfile", "type": "object" + } + }, + "required": [ + "enabled", + "runAsUser", + "runAsGroup", + "runAsNonRoot", + "allowPrivilegeEscalation", + "capabilities", + "seccompProfile" + ], + "title": "containerSecurityContext" + }, + "enabled": { + "default": true, + "description": "Deploy the built-in PostgreSQL instance", + "required": [], + "title": "enabled", + "type": "boolean" + }, + "extraEnvVars": { + "description": "Extra environment variables for the PostgreSQL container", + "items": { + "required": [] + }, + "required": [], + "title": "extraEnvVars", + "type": "array" + }, + "image": { + "additionalProperties": false, + "properties": { + "pullPolicy": { + "default": "IfNotPresent", + "description": "PostgreSQL image pull policy", + "required": [], + "title": "pullPolicy", + "type": "string" }, - "sessionAffinity": { - "default": "None", - "description": "Session affinity", + "registry": { + "default": "docker.io", + "description": "PostgreSQL image registry", "required": [], - "title": "sessionAffinity", + "title": "registry", "type": "string" }, - "sessionAffinityConfig": { - "additionalProperties": true, - "description": "Session affinity config", + "repository": { + "default": "postgres", + "description": "PostgreSQL image repository (docker official image)", "required": [], - "title": "sessionAffinityConfig" + "title": "repository", + "type": "string" }, - "type": { - "default": "ClusterIP", - "description": "Service type", + "tag": { + "default": "17", + "description": "PostgreSQL image tag", "required": [], - "title": "type", + "title": "tag", "type": "string" } }, "required": [ - "type", - "ports", - "nodePorts", - "clusterIP", - "loadBalancerIP", - "loadBalancerSourceRanges", - "externalTrafficPolicy", - "sessionAffinity", - "extraPorts" + "registry", + "repository", + "tag", + "pullPolicy" ], - "title": "service", + "title": "image", "type": "object" }, - "sidecars": { - "description": "Sidecar containers (tpl-rendered)", - "items": { - "required": [] - }, + "nodeSelector": { + "additionalProperties": true, + "description": "PostgreSQL node selector", "required": [], - "title": "sidecars", - "type": "array" + "title": "nodeSelector" }, - "startupProbe": { + "persistence": { "additionalProperties": false, "properties": { "enabled": { "default": true, - "description": "Enable the startup probe (first boot runs database creation, migrations and provisioning)", + "description": "Persist PostgreSQL data", "required": [], "title": "enabled", "type": "boolean" }, - "failureThreshold": { - "default": 60, - "required": [], - "title": "failureThreshold", - "type": "integer" - }, - "initialDelaySeconds": { - "default": 5, - "required": [], - "title": "initialDelaySeconds", - "type": "integer" - }, - "periodSeconds": { - "default": 5, + "existingClaim": { + "default": "", + "description": "Use an existing PVC instead of creating one", "required": [], - "title": "periodSeconds", - "type": "integer" + "title": "existingClaim", + "type": "string" }, - "successThreshold": { - "default": 1, + "size": { + "default": "8Gi", + "description": "PVC size", "required": [], - "title": "successThreshold", - "type": "integer" + "title": "size", + "type": "string" }, - "timeoutSeconds": { - "default": 5, + "storageClass": { + "default": "", + "description": "PVC storage class (\"\" = cluster default; global.defaultStorageClass wins)", "required": [], - "title": "timeoutSeconds", - "type": "integer" + "title": "storageClass", + "type": "string" } }, "required": [ "enabled", - "initialDelaySeconds", - "periodSeconds", - "timeoutSeconds", - "failureThreshold", - "successThreshold" + "size", + "storageClass", + "existingClaim" ], - "title": "startupProbe", + "title": "persistence", "type": "object" }, - "terminationGracePeriodSeconds": { - "default": 30, - "description": "Pod termination grace period (server-core tears down within ~10s after signal)", - "required": [], - "title": "terminationGracePeriodSeconds", - "type": "integer" - }, - "tolerations": { - "description": "Tolerations", - "items": { - "required": [] - }, - "required": [], - "title": "tolerations", - "type": "array" - }, - "topologySpreadConstraints": { - "description": "Topology spread constraints (a missing labelSelector is filled with the pod's selector labels)", - "items": { - "required": [] - }, - "required": [], - "title": "topologySpreadConstraints", - "type": "array" - }, - "trustProxy": { - "default": "1", - "description": "TRUST_PROXY setting. The chart defaults to one trusted hop (the ingress),\nnot authup's spoofable trust-everything default", - "required": [], - "title": "trustProxy", - "type": "string" - }, - "trustedOrigins": { - "description": "Additional trusted first-party app origins (TRUSTED_ORIGINS). Each listed\norigin can obtain full-permission tokens via the per-realm web client.\nList or comma-separated string; tpl-rendered.", - "items": { - "required": [] - }, - "required": [], - "title": "trustedOrigins", - "type": "array" - }, - "trustedOriginsAppendUI": { - "default": true, - "description": "Automatically append the client-console UI origin to TRUSTED_ORIGINS (removes the\nmost common dead-login misconfiguration)", - "required": [], - "title": "trustedOriginsAppendUI", - "type": "boolean" - }, - "updateStrategy": { + "podSecurityContext": { "additionalProperties": true, - "description": "Deployment update strategy", + "description": "PostgreSQL pod security context", "properties": { - "type": { - "default": "RollingUpdate", + "enabled": { + "default": true, "required": [], - "title": "type", - "type": "string" + "title": "enabled", + "type": "boolean" + }, + "fsGroup": { + "default": 999, + "required": [], + "title": "fsGroup", + "type": "integer" } }, "required": [ - "type" + "enabled", + "fsGroup" ], - "title": "updateStrategy" - } - }, - "required": [ - "enabled", - "replicaCount", - "publicUrl", - "trustedOrigins", - "trustedOriginsAppendUI", - "trustProxy", - "features", - "mfa", - "configuration", - "existingConfigmap", - "provisioning", - "migration", - "command", - "args", - "extraEnvVars", - "extraEnvVarsCM", - "extraEnvVarsSecret", - "extraVolumes", - "extraVolumeMounts", - "initContainers", - "sidecars", - "hostAliases", - "terminationGracePeriodSeconds", - "revisionHistoryLimit", - "disableRestartOnChanges", - "startupProbe", - "livenessProbe", - "readinessProbe", - "tolerations", - "podAntiAffinityPreset", - "topologySpreadConstraints", - "priorityClassName", - "schedulerName", - "service", - "ingress", - "route", - "metrics", - "pdb", - "autoscaling", - "networkPolicy" - ], - "title": "server", - "type": "object" - }, - "serviceAccount": { - "additionalProperties": false, - "description": "#", - "properties": { - "annotations": { - "additionalProperties": true, - "description": "ServiceAccount annotations (tpl-rendered)", - "required": [], - "title": "annotations" - }, - "automountServiceAccountToken": { - "default": false, - "description": "Automount the service account token", - "required": [], - "title": "automountServiceAccountToken", - "type": "boolean" - }, - "create": { - "default": true, - "description": "Create a ServiceAccount (shared by both services)", - "required": [], - "title": "create", - "type": "boolean" - }, - "name": { - "default": "", - "description": "ServiceAccount name (\"\" = generated from the fullname)", - "required": [], - "title": "name", - "type": "string" - } - }, - "required": [ - "create", - "name", - "automountServiceAccountToken" - ], - "title": "serviceAccount", - "type": "object" - }, - "smtp": { - "additionalProperties": false, - "description": "#", - "properties": { - "connectionString": { - "default": "", - "description": "SMTP connection string (smtp(s)://user:pass", - "required": [], - "title": "connectionString", - "type": "string" + "title": "podSecurityContext" }, - "existingSecret": { - "default": "", - "description": "Existing secret holding the SMTP connection string (tpl-rendered)", + "resources": { + "additionalProperties": true, + "description": "PostgreSQL container resources", + "properties": { + "limits": { + "additionalProperties": true, + "properties": { + "memory": { + "default": "1Gi", + "required": [], + "title": "memory", + "type": "string" + } + }, + "required": [ + "memory" + ], + "title": "limits" + }, + "requests": { + "additionalProperties": true, + "properties": { + "cpu": { + "default": "100m", + "required": [], + "title": "cpu", + "type": "string" + }, + "memory": { + "default": "256Mi", + "required": [], + "title": "memory", + "type": "string" + } + }, + "required": [ + "cpu", + "memory" + ], + "title": "requests" + } + }, "required": [], - "title": "existingSecret", - "type": "string" + "title": "resources" }, - "existingSecretKey": { - "default": "smtp-connection-string", - "description": "Key inside smtp.existingSecret holding the connection string", + "tolerations": { + "description": "PostgreSQL tolerations", + "items": { + "required": [] + }, "required": [], - "title": "existingSecretKey", - "type": "string" + "title": "tolerations", + "type": "array" } }, "required": [ - "connectionString", - "existingSecret", - "existingSecretKey" + "enabled", + "image", + "auth", + "persistence", + "tolerations", + "extraEnvVars" ], - "title": "smtp", + "title": "postgresql", "type": "object" }, - "ui": { + "server": { "additionalProperties": false, "description": "#", "properties": { @@ -2462,13 +2121,6 @@ "required": [], "title": "affinity" }, - "apiUrl": { - "default": "", - "description": "Browser-facing server-core URL (NUXT_PUBLIC_API_URL). \"\" = the server public URL.\nMust be reachable from the user's browser, never a cluster-internal DNS name", - "required": [], - "title": "apiUrl", - "type": "string" - }, "args": { "description": "Override the container args", "items": { @@ -2486,7 +2138,7 @@ "properties": { "enabled": { "default": false, - "description": "Enable HPA for the UI", + "description": "Enable HPA for server-core (requires a configured cache)", "required": [], "title": "enabled", "type": "boolean" @@ -2548,13 +2200,20 @@ }, "config": { "additionalProperties": true, - "description": "Extra environment variables rendered literally into the env ConfigMap", + "description": "Extra environment variables rendered literally into the env ConfigMap\n(map of NAME: value) for options without first-class values", "required": [], "title": "config" }, + "configuration": { + "default": "", + "description": "Content of an authup.server.core.conf mounted into the working directory\nfor file-only options (middleware objects, per-field SMTP, CORS allowlist).\nEnvironment variables always win over file values.", + "required": [], + "title": "configuration", + "type": "string" + }, "containerSecurityContext": { "additionalProperties": true, - "description": "Container security context (same root-image caveat as the server)", + "description": "Container security context. The upstream image runs as root and needs a\nwritable npm cache; the chart mounts emptyDirs at /usr/src/app/writable and\n/tmp to keep readOnlyRootFilesystem viable.", "properties": { "allowPrivilegeEscalation": { "default": false, @@ -2652,26 +2311,33 @@ }, "customStartupProbe": { "additionalProperties": true, - "description": "Custom startup probe", + "description": "Custom startup probe overriding the structured one", "required": [], "title": "customStartupProbe" }, "disableRestartOnChanges": { "default": false, - "description": "Disable the checksum annotations that roll pods on config changes", + "description": "Disable the checksum annotations that roll pods on config/secret changes", "required": [], "title": "disableRestartOnChanges", "type": "boolean" }, "enabled": { "default": true, - "description": "Deploy the client-console admin UI (false = headless IdP)", + "description": "Deploy the server-core service", "required": [], "title": "enabled", "type": "boolean" }, + "existingConfigmap": { + "default": "", + "description": "Existing ConfigMap holding authup.server.core.conf (tpl-rendered)", + "required": [], + "title": "existingConfigmap", + "type": "string" + }, "extraEnvVars": { - "description": "Extra environment variables for the UI container", + "description": "Extra environment variables for the server container", "items": { "required": [] }, @@ -2711,6 +2377,39 @@ "title": "extraVolumes", "type": "array" }, + "features": { + "additionalProperties": false, + "properties": { + "emailVerification": { + "default": false, + "description": "Enable email verification (EMAIL_VERIFICATION_ENABLED; requires SMTP)", + "required": [], + "title": "emailVerification", + "type": "boolean" + }, + "passwordRecovery": { + "default": false, + "description": "Enable password recovery (PASSWORD_RECOVERY_ENABLED; requires SMTP)", + "required": [], + "title": "passwordRecovery", + "type": "boolean" + }, + "registration": { + "default": false, + "description": "Enable self-service user registration (REGISTRATION_ENABLED)", + "required": [], + "title": "registration", + "type": "boolean" + } + }, + "required": [ + "registration", + "passwordRecovery", + "emailVerification" + ], + "title": "features", + "type": "object" + }, "hostAliases": { "description": "Pod host aliases", "items": { @@ -2725,7 +2424,7 @@ "properties": { "annotations": { "additionalProperties": true, - "description": "Ingress annotations (tpl-rendered)", + "description": "Ingress annotations (tpl-rendered). Token responses are large; with\ningress-nginx consider proxy-buffer-size 16k+.", "required": [], "title": "annotations" }, @@ -2738,7 +2437,7 @@ }, "enabled": { "default": false, - "description": "Enable ingress for the UI", + "description": "Enable ingress for server-core. NOTE: this also exposes the\nUNAUTHENTICATED /metrics endpoint publicly — block it at the ingress\ncontroller or disable it via server.configuration\n(\"middlewarePrometheus: false\") when it is not scraped", "required": [], "title": "enabled", "type": "boolean" @@ -2781,7 +2480,7 @@ }, "hostname": { "default": "", - "description": "Ingress hostname (tpl-rendered); also drives the derived UI public URL", + "description": "Ingress hostname (tpl-rendered); also drives the derived PUBLIC_URL", "required": [], "title": "hostname", "type": "string" @@ -2809,7 +2508,7 @@ }, "tls": { "default": false, - "description": "Enable TLS for the hostname", + "description": "Enable TLS for the hostname (secret \u003chostname\u003e-tls unless extraTls overrides)", "required": [], "title": "tls", "type": "boolean" @@ -2840,69 +2539,226 @@ "title": "initContainers", "type": "array" }, - "internalApiUrl": { - "default": "", - "description": "Server-side (SSR) API URL override (NUXT_API_URL), e.g. the in-cluster\nservice URL to keep SSR traffic off the ingress", - "required": [], - "title": "internalApiUrl", - "type": "string" - }, "lifecycleHooks": { "additionalProperties": true, "description": "Container lifecycle hooks", "required": [], "title": "lifecycleHooks" }, - "livenessProbe": { + "livenessProbe": { + "additionalProperties": false, + "properties": { + "enabled": { + "default": true, + "description": "Enable the liveness probe (GET / status endpoint)", + "required": [], + "title": "enabled", + "type": "boolean" + }, + "failureThreshold": { + "default": 3, + "required": [], + "title": "failureThreshold", + "type": "integer" + }, + "initialDelaySeconds": { + "default": 0, + "required": [], + "title": "initialDelaySeconds", + "type": "integer" + }, + "periodSeconds": { + "default": 30, + "required": [], + "title": "periodSeconds", + "type": "integer" + }, + "successThreshold": { + "default": 1, + "required": [], + "title": "successThreshold", + "type": "integer" + }, + "timeoutSeconds": { + "default": 5, + "required": [], + "title": "timeoutSeconds", + "type": "integer" + } + }, + "required": [ + "enabled", + "initialDelaySeconds", + "periodSeconds", + "timeoutSeconds", + "failureThreshold", + "successThreshold" + ], + "title": "livenessProbe", + "type": "object" + }, + "metrics": { + "additionalProperties": false, + "properties": { + "serviceMonitor": { + "additionalProperties": false, + "description": "The /metrics endpoint is UNAUTHENTICATED; keep it off the public ingress", + "properties": { + "enabled": { + "default": false, + "description": "Create a prometheus-operator ServiceMonitor scraping /metrics", + "required": [], + "title": "enabled", + "type": "boolean" + }, + "honorLabels": { + "default": false, + "description": "Honor labels", + "required": [], + "title": "honorLabels", + "type": "boolean" + }, + "interval": { + "default": "30s", + "description": "Scrape interval", + "required": [], + "title": "interval", + "type": "string" + }, + "jobLabel": { + "default": "", + "description": "Job label", + "required": [], + "title": "jobLabel", + "type": "string" + }, + "labels": { + "additionalProperties": true, + "description": "ServiceMonitor labels", + "required": [], + "title": "labels" + }, + "metricRelabelings": { + "description": "Metric relabelings", + "items": { + "required": [] + }, + "required": [], + "title": "metricRelabelings", + "type": "array" + }, + "namespace": { + "default": "", + "description": "ServiceMonitor namespace (\"\" = release namespace)", + "required": [], + "title": "namespace", + "type": "string" + }, + "relabelings": { + "description": "Relabelings", + "items": { + "required": [] + }, + "required": [], + "title": "relabelings", + "type": "array" + }, + "scrapeTimeout": { + "default": "", + "description": "Scrape timeout", + "required": [], + "title": "scrapeTimeout", + "type": "string" + } + }, + "required": [ + "enabled", + "namespace", + "interval", + "scrapeTimeout", + "honorLabels", + "metricRelabelings", + "relabelings", + "jobLabel" + ], + "title": "serviceMonitor", + "type": "object" + } + }, + "required": [ + "serviceMonitor" + ], + "title": "metrics", + "type": "object" + }, + "mfa": { "additionalProperties": false, "properties": { "enabled": { - "default": true, - "description": "Enable the liveness probe", + "default": false, + "description": "Enable multi-factor authentication (MFA_ENABLED)", "required": [], "title": "enabled", "type": "boolean" }, - "failureThreshold": { + "required": { + "default": false, + "description": "Require MFA for every user (MFA_REQUIRED; needs mfa.enabled)", + "required": [], + "title": "required", + "type": "boolean" + } + }, + "required": [ + "enabled", + "required" + ], + "title": "mfa", + "type": "object" + }, + "migration": { + "additionalProperties": false, + "properties": { + "backoffLimit": { "default": 3, + "description": "Job backoff limit", "required": [], - "title": "failureThreshold", + "title": "backoffLimit", "type": "integer" }, - "initialDelaySeconds": { - "default": 0, + "enabled": { + "default": false, + "description": "Run `server/core migration run` as a pre-upgrade hook Job. Recommended\nfor multi-replica deployments (serializes DDL before pods roll). Fresh\ninstalls migrate at boot regardless.", "required": [], - "title": "initialDelaySeconds", - "type": "integer" + "title": "enabled", + "type": "boolean" }, - "periodSeconds": { - "default": 30, + "podAnnotations": { + "additionalProperties": true, + "description": "Job pod annotations", "required": [], - "title": "periodSeconds", - "type": "integer" + "title": "podAnnotations" }, - "successThreshold": { - "default": 1, + "resources": { + "additionalProperties": true, + "description": "Job resources ({} = server resources defaults)", "required": [], - "title": "successThreshold", - "type": "integer" + "title": "resources" }, - "timeoutSeconds": { - "default": 5, + "ttlSecondsAfterFinished": { + "default": 300, + "description": "Delete the Job this many seconds after it finishes (\"\" = keep)", "required": [], - "title": "timeoutSeconds", + "title": "ttlSecondsAfterFinished", "type": "integer" } }, "required": [ "enabled", - "initialDelaySeconds", - "periodSeconds", - "timeoutSeconds", - "failureThreshold", - "successThreshold" + "backoffLimit", + "ttlSecondsAfterFinished" ], - "title": "livenessProbe", + "title": "migration", "type": "object" }, "networkPolicy": { @@ -2910,7 +2766,7 @@ "properties": { "allowExternal": { "default": true, - "description": "Allow ingress from anywhere", + "description": "Allow ingress from anywhere. When false, only same-namespace pods,\nthe release's UI pods and the configured selectors may connect — add\nyour ingress controller via ingressNSMatchLabels/ingressPodMatchLabels", "required": [], "title": "allowExternal", "type": "boolean" @@ -2924,7 +2780,7 @@ }, "enabled": { "default": false, - "description": "Create a NetworkPolicy for the UI", + "description": "Create a NetworkPolicy for server-core", "required": [], "title": "enabled", "type": "boolean" @@ -2981,7 +2837,7 @@ "properties": { "create": { "default": false, - "description": "Create a PodDisruptionBudget for the UI", + "description": "Create a PodDisruptionBudget for server-core", "required": [], "title": "create", "type": "boolean" @@ -3058,9 +2914,48 @@ "title": "priorityClassName", "type": "string" }, + "provisioning": { + "additionalProperties": false, + "properties": { + "enabled": { + "default": false, + "description": "Mount provisioning files consumed at boot (fail-closed: an invalid file aborts startup)", + "required": [], + "title": "enabled", + "type": "boolean" + }, + "existingConfigMap": { + "default": "", + "description": "Existing ConfigMap with provisioning files (tpl-rendered)", + "required": [], + "title": "existingConfigMap", + "type": "string" + }, + "existingSecret": { + "default": "", + "description": "Existing Secret with provisioning files (tpl-rendered; takes\nprecedence — use for provisioning content that carries credentials)", + "required": [], + "title": "existingSecret", + "type": "string" + }, + "files": { + "additionalProperties": true, + "description": "Map of filename -\u003e file content (tpl-rendered; the extension selects\nthe reader). Lands in a ConfigMap — credential-bearing provisioning\ncontent belongs in existingSecret instead", + "required": [], + "title": "files" + } + }, + "required": [ + "enabled", + "existingConfigMap", + "existingSecret" + ], + "title": "provisioning", + "type": "object" + }, "publicUrl": { "default": "", - "description": "Public URL of the UI (NUXT_PUBLIC_PUBLIC_URL). \"\" = derived from ui.ingress", + "description": "Public URL of server-core (PUBLIC_URL) — the OIDC issuer origin.\n\"\" = derived from server.ingress when enabled. Changing it later breaks\nenrolled WebAuthn credentials and the OIDC issuer.", "required": [], "title": "publicUrl", "type": "string" @@ -3070,7 +2965,7 @@ "properties": { "enabled": { "default": true, - "description": "Enable the readiness probe", + "description": "Enable the readiness probe (GET / status endpoint)", "required": [], "title": "enabled", "type": "boolean" @@ -3119,20 +3014,20 @@ }, "replicaCount": { "default": 1, - "description": "Number of UI replicas (fully stateless, scale freely)", + "description": "Number of server-core replicas (values \u003e 1 REQUIRE a configured cache)", "required": [], "title": "replicaCount", "type": "integer" }, "resources": { "additionalProperties": true, - "description": "UI container resources", + "description": "Server container resources", "properties": { "limits": { "additionalProperties": true, "properties": { "memory": { - "default": "512Mi", + "default": "2Gi", "required": [], "title": "memory", "type": "string" @@ -3147,13 +3042,13 @@ "additionalProperties": true, "properties": { "cpu": { - "default": "100m", + "default": "250m", "required": [], "title": "cpu", "type": "string" }, "memory": { - "default": "256Mi", + "default": "512Mi", "required": [], "title": "memory", "type": "string" @@ -3187,13 +3082,13 @@ }, "enabled": { "default": false, - "description": "Create a Gateway API HTTPRoute for the UI", + "description": "Create a Gateway API HTTPRoute for server-core", "required": [], "title": "enabled", "type": "boolean" }, "hostnames": { - "description": "Route hostnames ([] = derived from ui.publicUrl / ingress hostname)", + "description": "Route hostnames ([] = derived from server.publicUrl / ingress hostname)", "items": { "required": [] }, @@ -3357,13 +3252,13 @@ "properties": { "enabled": { "default": true, - "description": "Enable the startup probe", + "description": "Enable the startup probe (first boot runs database creation, migrations and provisioning)", "required": [], "title": "enabled", "type": "boolean" }, "failureThreshold": { - "default": 24, + "default": 60, "required": [], "title": "failureThreshold", "type": "integer" @@ -3406,7 +3301,7 @@ }, "terminationGracePeriodSeconds": { "default": 30, - "description": "Pod termination grace period", + "description": "Pod termination grace period (server-core tears down within ~10s after signal)", "required": [], "title": "terminationGracePeriodSeconds", "type": "integer" @@ -3421,7 +3316,7 @@ "type": "array" }, "topologySpreadConstraints": { - "description": "Topology spread constraints", + "description": "Topology spread constraints (a missing labelSelector is filled with the pod's selector labels)", "items": { "required": [] }, @@ -3429,6 +3324,29 @@ "title": "topologySpreadConstraints", "type": "array" }, + "trustProxy": { + "default": "1", + "description": "TRUST_PROXY setting. The chart defaults to one trusted hop (the ingress),\nnot authup's spoofable trust-everything default", + "required": [], + "title": "trustProxy", + "type": "string" + }, + "trustedOrigins": { + "description": "Additional trusted first-party app origins (TRUSTED_ORIGINS). Each listed\norigin can obtain full-permission tokens via the per-realm web client.\nList or comma-separated string; tpl-rendered.", + "items": { + "required": [] + }, + "required": [], + "title": "trustedOrigins", + "type": "array" + }, + "trustedOriginsAppendAdminConsole": { + "default": true, + "description": "Automatically append the client-admin-console UI origin to TRUSTED_ORIGINS (removes the\nmost common dead-login misconfiguration)", + "required": [], + "title": "trustedOriginsAppendAdminConsole", + "type": "boolean" + }, "updateStrategy": { "additionalProperties": true, "description": "Deployment update strategy", @@ -3450,8 +3368,15 @@ "enabled", "replicaCount", "publicUrl", - "apiUrl", - "internalApiUrl", + "trustedOrigins", + "trustedOriginsAppendAdminConsole", + "trustProxy", + "features", + "mfa", + "configuration", + "existingConfigmap", + "provisioning", + "migration", "command", "args", "extraEnvVars", @@ -3476,11 +3401,86 @@ "service", "ingress", "route", + "metrics", "pdb", "autoscaling", "networkPolicy" ], - "title": "ui", + "title": "server", + "type": "object" + }, + "serviceAccount": { + "additionalProperties": false, + "description": "#", + "properties": { + "annotations": { + "additionalProperties": true, + "description": "ServiceAccount annotations (tpl-rendered)", + "required": [], + "title": "annotations" + }, + "automountServiceAccountToken": { + "default": false, + "description": "Automount the service account token", + "required": [], + "title": "automountServiceAccountToken", + "type": "boolean" + }, + "create": { + "default": true, + "description": "Create a ServiceAccount (shared by both services)", + "required": [], + "title": "create", + "type": "boolean" + }, + "name": { + "default": "", + "description": "ServiceAccount name (\"\" = generated from the fullname)", + "required": [], + "title": "name", + "type": "string" + } + }, + "required": [ + "create", + "name", + "automountServiceAccountToken" + ], + "title": "serviceAccount", + "type": "object" + }, + "smtp": { + "additionalProperties": false, + "description": "#", + "properties": { + "connectionString": { + "default": "", + "description": "SMTP connection string (smtp(s)://user:pass", + "required": [], + "title": "connectionString", + "type": "string" + }, + "existingSecret": { + "default": "", + "description": "Existing secret holding the SMTP connection string (tpl-rendered)", + "required": [], + "title": "existingSecret", + "type": "string" + }, + "existingSecretKey": { + "default": "smtp-connection-string", + "description": "Key inside smtp.existingSecret holding the connection string", + "required": [], + "title": "existingSecretKey", + "type": "string" + } + }, + "required": [ + "connectionString", + "existingSecret", + "existingSecretKey" + ], + "title": "smtp", "type": "object" }, "useHelmHooks": { @@ -3797,7 +3797,7 @@ "externalRedis", "smtp", "server", - "ui", + "adminConsole", "serviceAccount" ], "type": "object" diff --git a/charts/authup/values.yaml b/charts/authup/values.yaml index b208577..2cab0e8 100644 --- a/charts/authup/values.yaml +++ b/charts/authup/values.yaml @@ -406,9 +406,9 @@ server: # origin can obtain full-permission tokens via the per-realm web client. # List or comma-separated string; tpl-rendered. trustedOrigins: [] - # -- Automatically append the client-console UI origin to TRUSTED_ORIGINS (removes the + # -- Automatically append the client-admin-console UI origin to TRUSTED_ORIGINS (removes the # most common dead-login misconfiguration) - trustedOriginsAppendUI: true + trustedOriginsAppendAdminConsole: true # -- TRUST_PROXY setting. The chart defaults to one trusted hop (the ingress), # not authup's spoofable trust-everything default trustProxy: "1" @@ -757,14 +757,14 @@ server: # -- Extra egress rules extraEgress: [] -## @section client-console (admin UI) +## @section client-admin-console (admin UI) -ui: - # -- Deploy the client-console admin UI (false = headless IdP) +adminConsole: + # -- Deploy the client-admin-console admin UI (false = headless IdP) enabled: true # -- Number of UI replicas (fully stateless, scale freely) replicaCount: 1 - # -- Public URL of the UI (NUXT_PUBLIC_PUBLIC_URL). "" = derived from ui.ingress + # -- Public URL of the UI (NUXT_PUBLIC_PUBLIC_URL). "" = derived from adminConsole.ingress publicUrl: "" # -- Browser-facing server-core URL (NUXT_PUBLIC_API_URL). "" = the server public URL. # Must be reachable from the user's browser, never a cluster-internal DNS name @@ -982,7 +982,7 @@ ui: route: # -- Create a Gateway API HTTPRoute for the UI enabled: false - # -- Route hostnames ([] = derived from ui.publicUrl / ingress hostname) + # -- Route hostnames ([] = derived from adminConsole.publicUrl / ingress hostname) hostnames: [] # -- Gateway parentRefs parentRefs: [] From 9be077a9883649cbc1068e58233b1ab85fd549de Mon Sep 17 00:00:00 2001 From: tada5hi Date: Tue, 4 Aug 2026 09:27:07 +0200 Subject: [PATCH 4/5] refactor(authup)!: rename the admin console pod container to admin-console The container was the last identifier in the admin console Deployment still carrying the old name, while the server and migration workloads already name their containers after the component. Raised in review on PR #3. Documented in BREAKING.md: 'kubectl logs -c ui' and log pipelines selecting on the container name need updating. --- charts/authup/BREAKING.md | 3 +++ charts/authup/templates/admin-console/deployment.yaml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/charts/authup/BREAKING.md b/charts/authup/BREAKING.md index 0f8fe35..a505ac0 100644 --- a/charts/authup/BREAKING.md +++ b/charts/authup/BREAKING.md @@ -16,6 +16,9 @@ dedicated OAuth2 client (authup/authup#3371). (Deployment, Service, Ingress, ...). Helm re-creates them on upgrade; expect a brief admin-UI rollout and update anything referencing the old Service name directly. +- The pod container is renamed `ui` -> `admin-console`, matching the `server` + and `migration` containers. Update any `kubectl logs -c ui` / `exec -c ui` + invocation and any log pipeline selecting on the container name. - The admin UI container now starts with `client/admin-console start` and logs in against the per-realm `admin-console` OAuth2 client. Requires an authup image containing authup/authup#3370 + #3371; older images only know diff --git a/charts/authup/templates/admin-console/deployment.yaml b/charts/authup/templates/admin-console/deployment.yaml index b1cd407..5b3e710 100644 --- a/charts/authup/templates/admin-console/deployment.yaml +++ b/charts/authup/templates/admin-console/deployment.yaml @@ -64,7 +64,7 @@ spec: initContainers: {{- include "authup.tplvalues.render" (dict "value" .Values.adminConsole.initContainers "context" $) | nindent 8 }} {{- end }} containers: - - name: ui + - name: admin-console image: {{ include "authup.appImage" . }} imagePullPolicy: {{ .Values.image.pullPolicy }} {{- if .Values.adminConsole.containerSecurityContext.enabled }} From 24074ed2b2ed0475c8dabd94e534ca9752972a21 Mon Sep 17 00:00:00 2001 From: tada5hi Date: Tue, 4 Aug 2026 09:27:18 +0200 Subject: [PATCH 5/5] chore: ignore local agent plan files .agents/plans/ holds working design notes that stay local to a checkout, and the docs and schema drift gates in CI are 'git status --porcelain' based, so untracked files there would trip them. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 166f3a6..89dce56 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .DS_Store .idea/ .vscode/ +.agents/plans/ *.tgz .cr-release-packages/ .cr-index/