Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .agents/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ editing templates or values.
`server.configuration` file mount). Never mirror authup's config schema in
templates (Authelia's 714-line configMap treadmill is the cautionary tale).
`server.config` keys colliding with first-class env names fail the render.
The ONE mirrored schema is the theme manifest (`server.theme.title` /
`logo` / `tokens` / ... compose `theme.json`), and it earns the exception
on three counts: the file is a fixed 8-key document rather than a growing
config surface, authup fails the BOOT on an unknown key or a malformed
token so a typo has no cheaper detector, and the alternative is a JSON
blob inside a YAML string with no schema at all. It stays worth it only
while the manifest stays small: `files` remains the escape hatch, and a
hand-written `theme.json` there is still supported (the two are mutually
exclusive by validation).
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`
Expand Down
24 changes: 21 additions & 3 deletions .agents/references/authup.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Repo: https://github.com/authup/authup (local checkout commonly at
`/opt/projects/authup/authup`). The chart encodes facts about the app; verify
against these sources when authup releases change behavior. Pinned against the
v1.0.0-beta.58 line (chart `appVersion`).
v1.0.0-beta.62 line (chart `appVersion`).

## Image / entrypoint contract

Expand All @@ -14,6 +14,7 @@ v1.0.0-beta.58 line (chart `appVersion`).
| 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`/`<version>`/`beta`/`next` tags | `.github/workflows/release.yml`, `docker-nightly.yml` | `image.tag` defaults to `Chart.AppVersion` |
| `authup` CLI supervisor NOT routable through the entrypoint | `entrypoint.sh` case statement | chart never offers a combined pod |
| An unknown service arg EXITS 1 since beta.59 (it used to exit 0 and start nothing); `client/web` was renamed `client/admin-console` with no alias | `entrypoint.sh` `*)` branch | chart already passes `client/admin-console` |

## server-core env surface

Expand All @@ -29,9 +30,13 @@ Docs mirror: `docs/src/guide/deployment/configuration-server-core*.md`.
| `SMTP` (URL form; per-field SMTP is config-file-only) | `secret-smtp.yaml` |
| `PUBLIC_URL`, `TRUSTED_ORIGINS`, `TRUST_PROXY` (app default trusts every hop; chart pins "1") | `_urls.tpl` + `authup.server.configEnv` |
| `REGISTRATION_ENABLED`, `PASSWORD_RECOVERY_ENABLED`, `EMAIL_VERIFICATION_ENABLED`, `MFA_ENABLED`, `MFA_REQUIRED` (strict booleans: unparsable value crashes boot) | `server.features.*` / `server.mfa.*`, always quoted |
| `ACCOUNT_CONSOLE_ENABLED` (beta.62, default true): serves the `/account` self-service SPA off the IdP origin | `server.features.accountConsole` |
| `THEME_DIRECTORY_PATH` / `THEME_FRAGMENTS_ENABLED` (beta.59, EXPERIMENTAL): operator theme for the two served consoles; manifest at `<root>/theme.json`, HTTP mount root is `<root>/assets` only | `server.theme.*` (the chart composes theme.json) |
| `AUTH_CONSOLE_PATH` / `ACCOUNT_CONSOLE_PATH`: substitute a whole console package, boot-asserted `CONTRACT_VERSION` | deliberately NOT first-class; `server.config` + `extraVolumes` escape hatch |
| `USER_ADMIN_PASSWORD(_RESET)`, `CLIENT_SYSTEM_ENABLED/SECRET(_RESET)` | `auth.*` values + chart-managed secret |
| `SECRETS_ENCRYPTION_KEY` (base64 32 bytes; write-once, removal with wrapped rows fails loud) | `auth.secretsEncryptionKey(+Enabled)`, never generated, never optional |
| Cross-field boot validations (throttle needs event log, mfaRequired needs mfaEnabled, KEK length) | mirrored as render-time guards in `templates/validations.yaml` |
| `TRUSTED_ORIGINS` rejects `**` in a host at boot since beta.59 (`config/origins.ts`, `patternHasGlobstarInAuthority`); a single `*` is a supported host wildcard | `authup.assertTrustedOrigin` in `_urls.tpl`, asserted after tpl rendering |

Config file: `authup.server.core.conf` in the process cwd
(`app/modules/config/read/fs.ts`; env always wins) -> `server.configuration` /
Expand All @@ -47,8 +52,10 @@ and dead): `apps/client-admin-console/nuxt.config.ts`,
`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:
`_admin-console-env.tpl`.
unsupported per `.agents/architecture.md` in the monorepo),
`NUXT_PUBLIC_CLIENT_ID` (beta.59+, defaults to the per-realm built-in
`admin-console` client; fork-only override, reachable via
`adminConsole.config`). Chart counterpart: `_admin-console-env.tpl`.

## Operational contract

Expand All @@ -65,3 +72,14 @@ unsupported per `.agents/architecture.md` in the monorepo). Chart counterpart:
ServiceMonitor targets the Service; ingress warning in values/NOTES.
- In-process cron sweepers (oauth2-cleaner, event-cleaner) are idempotent
deletes; no leader election needed.
- Reserved client names: `admin-console` and `account-console` are provisioned
as built-in system clients in EVERY realm and take over a pre-existing client
of that name (beta.59). The shared per-realm `web` client was removed in the
same release; `TRUSTED_ORIGINS` now feeds the system clients' redirect
allowlists. NOTES warns against declaring either name in
`server.provisioning`.
- beta.60 ships a heavy migration (140 indexes, MySQL `varchar(36)` ->
`varchar(255)` table rewrites, three dropped tables) and beta.62 adds a
unique constraint on `auth_identity_provider_accounts` that ABORTS the boot
on pre-existing duplicates. Both are arguments for
`server.migration.enabled` on an upgrade, not just for multi-replica.
9 changes: 9 additions & 0 deletions .agents/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,17 @@ helm template t charts/authup --set server.publicUrl=auth.example.com # s
helm template t charts/authup --set postgresql.enabled=false --set externalDatabase.host=db # extdb w/o password
helm template t charts/authup --set server.ingress.enabled=true # ingress w/o hostname
helm template t charts/authup --set server.config.PUBLIC_URL=http://x # first-class collision
helm template t charts/authup --set server.theme.enabled=true # theme with no carrier
helm template t charts/authup --set server.theme.enabled=true --set server.theme.title=X --set server.theme.existingConfigMap=cm # manifest + existing CM
helm template t charts/authup --set server.theme.enabled=true --set server.theme.logo=logo.svg # asset outside assets/
helm template t charts/authup --set server.theme.enabled=true --set server.theme.logo=assets/logo.svg # asset missing from files
helm template t charts/authup --set server.theme.enabled=true --set 'server.theme.tokens.--authup-bg=url(x)' # token value authup rejects
helm template t charts/authup --set 'server.trustedOrigins[0]=https://**.x' # globstar host
```

A single `*` host wildcard (`https://*.example.com`) must still RENDER: authup
supports it, only `**` is the allow-any-origin trap.

The generated `values.schema.json` must keep catching typos
(`--set server.replicaCountt=3` fails) while free-form maps stay open
(`--set server.config.X=y`, `--set server.resources.limits.cpu=1` succeed).
Expand Down
12 changes: 8 additions & 4 deletions charts/authup/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: Authup is an authentication & authorization system. This chart
with optional built-in PostgreSQL, MySQL and Valkey instances.
type: application
version: 0.2.0
appVersion: "1.0.0-beta.58"
appVersion: "1.0.0-beta.62"
kubeVersion: ">=1.25.0-0"
home: https://authup.org
icon: https://raw.githubusercontent.com/authup/helm/master/assets/icon.svg
Expand All @@ -31,7 +31,11 @@ annotations:
- name: Source
url: https://github.com/authup/helm
artifacthub.io/changes: |
- kind: fixed
description: Chart license corrected to Apache-2.0
- kind: changed
description: appVersion tracks authup 1.0.0-beta.62
- kind: added
description: Chart icon
description: server.theme manifest values compose theme.json (title, logo, tokens)
- kind: added
description: server.features.accountConsole toggles the /account self-service console
- kind: added
description: Trusted origins carrying "**" now fail the render, as authup fails the boot
72 changes: 64 additions & 8 deletions charts/authup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
# authup

![Version](https://img.shields.io/badge/Version-0.2.0?style=flat-square&color=informational) <!-- x-release-please-version -->
![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)
![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.0.0-beta.62](https://img.shields.io/badge/AppVersion-1.0.0--beta.62-informational?style=flat-square)

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.
surface, the server-rendered auth pages (login, consent, registration,
password recovery) and the `/account` self-service console
(`server.features.accountConsole`). This is the identity origin.
- **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
Expand Down Expand Up @@ -88,6 +89,53 @@ Notable operational facts (enforced or warned about by the chart):
`server.configuration` file. See the
[Authup configuration reference](https://authup.org).

## Theming the served consoles

Both consoles server-core serves (the auth pages and `/account`) are rebranded
from a directory the chart mounts read-only. Set the manifest as values and the
chart composes `theme.json` for you; `files` carries the assets it references:

```yaml
server:
theme:
enabled: true
title: Sign in to ACME
logo: assets/logo.svg
stylesheet: assets/theme.css
tokens:
# the accent the whole primary palette is mixed from
--authup-periwinkle: "#c0392b"
--authup-surface-card: "#ffffff"
tokensDark:
--authup-surface-card: "#201e1d"
files:
assets/logo.svg: |
<svg xmlns="http://www.w3.org/2000/svg" ...></svg>
assets/theme.css: |
.a-auth-shell-card { border: 1px solid var(--authup-surface-border); }
```

A colour in `tokens` wins in dark mode too, so surface colours belong in both
maps. The chart rejects at render time what Authup rejects at boot or answers
with a 404: an asset outside `assets/`, an asset no file provides, a token name
that is not a lowercase custom property, and a token value carrying `url(` or
`;`. Only `assets/` is served over HTTP, so `theme.json` is unreachable by
construction.

`existingConfigMap` replaces the whole mechanism when you need binary assets
(`binaryData`); it is mounted whole, so it must carry `theme.json` itself and
cannot be combined with the manifest values. `fragmentsEnabled` splices
`fragments/head.html` into the console `<head>` verbatim: raw operator markup
on the origin that holds your users' session cookies, hence opt-in.

> The theme directory is as sensitive as the config file: CSS there can restyle
> or cover the OAuth2 consent buttons. Never source it from somewhere a tenant
> or a lower-privileged CI job can write.

Theming is experimental upstream: the directory layout and the `theme*` options
may change in an Authup minor release. See the
[Authup theming guide](https://authup.org/guide/deployment/theming.html).

## GitOps / ArgoCD

The generate-once-keep-forever behavior of empty passwords relies on helm's
Expand Down Expand Up @@ -297,7 +345,7 @@ Kubernetes: `>=1.25.0-0`
| server.autoscaling.hpa.targetCPU | int | `75` | Target CPU utilization percentage |
| server.autoscaling.hpa.targetMemory | string | `""` | Target memory utilization percentage |
| server.command | list | `[]` | Override the container command |
| server.config | object | `{}` | Extra environment variables rendered literally into the env ConfigMap (map of NAME: value) for options without first-class values |
| server.config | object | `{}` | Extra environment variables rendered literally into the env ConfigMap (map of NAME: value) for options without first-class values, e.g. AUTH_CONSOLE_PATH / ACCOUNT_CONSOLE_PATH, which replace a served console with your own build (pair them with extraVolumes; the substituted package owns the login flow, so use server.theme for branding instead) |
| server.configuration | string | `""` | Content of an authup.server.core.conf mounted into the working directory for file-only options (middleware objects, per-field SMTP, CORS allowlist). Environment variables always win over file values. |
| server.containerSecurityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"enabled":true,"readOnlyRootFilesystem":false,"runAsNonRoot":false,"runAsUser":0,"seccompProfile":{"type":"RuntimeDefault"}}` | Container security context. The upstream image runs as root and needs a writable npm cache; the chart mounts emptyDirs at /usr/src/app/writable and /tmp to keep readOnlyRootFilesystem viable. |
| server.customLivenessProbe | object | `{}` | Custom liveness probe |
Expand All @@ -311,6 +359,7 @@ Kubernetes: `>=1.25.0-0`
| server.extraEnvVarsSecret | string | `""` | Extra Secret with environment variables (tpl-rendered name) |
| server.extraVolumeMounts | list | `[]` | Extra volume mounts (tpl-rendered) |
| server.extraVolumes | list | `[]` | Extra volumes (tpl-rendered) |
| server.features.accountConsole | bool | `true` | Serve the account self-service console at <publicUrl>/account (profile, password, authenticators, sessions, applications). ACCOUNT_CONSOLE_ENABLED; disable it when you run your own portal |
| server.features.emailVerification | bool | `false` | Enable email verification (EMAIL_VERIFICATION_ENABLED; requires SMTP) |
| server.features.passwordRecovery | bool | `false` | Enable password recovery (PASSWORD_RECOVERY_ENABLED; requires SMTP) |
| server.features.registration | bool | `false` | Enable self-service user registration (REGISTRATION_ENABLED) |
Expand Down Expand Up @@ -406,15 +455,22 @@ Kubernetes: `>=1.25.0-0`
| server.startupProbe.successThreshold | int | `1` | |
| server.startupProbe.timeoutSeconds | int | `5` | |
| server.terminationGracePeriodSeconds | int | `30` | Pod termination grace period (server-core tears down within ~10s after signal) |
| server.theme.enabled | bool | `false` | Mount an operator theme for the served consoles (the auth console and the account console). Requires an authup image that supports THEME_DIRECTORY_PATH; older images ignore it |
| server.theme.existingConfigMap | string | `""` | Existing ConfigMap holding the theme (tpl-rendered name). Use for binary assets, which cannot be expressed in files |
| server.theme.enabled | bool | `false` | Mount an operator theme for the served consoles (the auth console and the account console). Requires an authup image that supports THEME_DIRECTORY_PATH; older images ignore it. Experimental upstream: the directory layout and the theme* options may change in a minor release |
| server.theme.existingConfigMap | string | `""` | Existing ConfigMap holding the theme (tpl-rendered name). Use for binary assets, which cannot be expressed in files. Mounted whole, so it must carry theme.json itself and excludes the manifest values above |
| server.theme.existingConfigMapItems | list | `[]` | Key -> path projection for existingConfigMap, so its keys can land in subdirectories (e.g. [{key: theme-css, path: assets/theme.css}]). Empty mounts every key flat at the theme root |
| server.theme.files | object | `{}` | Map of path -> file content, relative to the theme root (tpl-rendered). Keys may carry a "/" ("assets/theme.css") and are projected into subdirectories. Only assets/ is served over HTTP. Text only — use existingConfigMap with binaryData for images |
| server.theme.favicon | string | `""` | Favicon path, relative to the theme root and under assets/ (e.g. assets/favicon.svg). Must be a key of files |
| server.theme.files | object | `{}` | Map of path -> file content, relative to the theme root (tpl-rendered). Keys may carry a "/" ("assets/theme.css") and are projected into subdirectories. Only assets/ is served over HTTP. Text only — use existingConfigMap with binaryData for images. Set theme.json here only when writing the manifest by hand instead of using the values above |
| server.theme.fragmentsEnabled | bool | `false` | Read fragments/head.html and splice it into the console <head>. Raw, unsanitized markup on the identity provider origin, so it is opt-in |
| server.theme.logo | string | `""` | Logo replacing the built-in mark on both consoles, under assets/. Painted into the existing mark's box, so it needs no sizing |
| server.theme.logoDark | string | `""` | Dark-mode logo variant, under assets/. Without it dark mode reuses logo, which disappears when the mark is drawn dark-on-light |
| server.theme.stylesheet | string | `""` | Stylesheet path, under assets/ and ending in .css. Linked last, so it beats the token block; it is unlayered, so set dark colors explicitly |
| server.theme.title | string | `""` | Document title of both served consoles ("" = authup's own) |
| server.theme.tokens | object | `{}` | authup-periwinkle alone recolors buttons, focus rings and links. A color set here also wins in dark mode: put surface colors in both tokens and tokensDark |
| server.theme.tokensDark | object | `{}` | CSS custom properties applied in dark mode only (tpl-rendered) |
| server.tolerations | list | `[]` | Tolerations |
| 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.trustedOrigins | list | `[]` | Additional trusted first-party app origins (TRUSTED_ORIGINS). Each entry is added to the redirect allowlist of the per-realm built-in system clients (admin-console, account-console), so any listed origin can complete a login and obtain a full-permission token. A host may carry a single "*" (https://*.example.com); "**" in a host is rejected by authup at boot. List or comma-separated string; tpl-rendered. |
| 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) |
Expand Down
Loading
Loading