diff --git a/infrastructure/production/fleet/authentik/README.md b/infrastructure/production/fleet/authentik/README.md
index 8e762ee..917c89c 100644
--- a/infrastructure/production/fleet/authentik/README.md
+++ b/infrastructure/production/fleet/authentik/README.md
@@ -70,6 +70,31 @@ Client secrets live in `authentik-secret` (`PORTAL_OIDC_CLIENT_SECRET`,
> post-render kustomize patch to make it durable). TODO.
## Still deferred
-- **Rebrand** of the "Powered by authentik" string (web-bundle `sed`, needs a
- root lifecycle override) — cosmetic; the *colors* are done via the brand CSS.
- Pin the **chart version** (currently latest → app `2026.5.2`).
+- **Durability:** the server-rebrand Deployment patch + the brand image-field
+ PATCH revert on a helm upgrade of Authentik — re-run them, or bake a custom
+ image / post-render kustomize patch.
+
+## Full visual rebrand (logo / favicon / background / footer) — APPLIED
+
+Brand custom CSS only reaches shadow DOM via CSS **vars** (so colors work), not
+the logo/favicon (deeper shadow root) or the "Powered by authentik" footer
+(light DOM). Those use dev's mechanism — real files + a bundle sed:
+
+- `web-assets/` — `dezky-logo.svg`, `dezky-favicon.svg`, `dezky-bg.svg` (carbon).
+- ConfigMap `authentik-web-assets` is built from `web-assets/`.
+- `server-rebrand.py` patches the **authentik-server** Deployment: an
+ initContainer copies `/web/dist` into an emptyDir, drops the 3 svgs into
+ `/web/dist/assets/icons/`, and seds `Powered by authentik` -> `Powered by
+ Dezky`. The server then serves the patched bundle.
+- The brand's `branding_logo` / `branding_favicon` /
+ `branding_default_flow_background` point at those served svgs (carried in
+ brand.yaml; if the blueprint leaves them default, PATCH the brand via API).
+
+Apply:
+```
+kubectl -n dezky-auth create configmap authentik-web-assets --from-file=web-assets/ --dry-run=client -o yaml | kubectl apply -f -
+kubectl -n dezky-auth get deploy authentik-server -o json | python3 server-rebrand.py | kubectl apply -f -
+```
+> CAVEAT: the server patch + brand PATCH revert on a helm upgrade of Authentik —
+> re-run them (or bake a custom image) for durability.
diff --git a/infrastructure/production/fleet/authentik/blueprints/brand.yaml b/infrastructure/production/fleet/authentik/blueprints/brand.yaml
index 3187d52..329ec9c 100644
--- a/infrastructure/production/fleet/authentik/blueprints/brand.yaml
+++ b/infrastructure/production/fleet/authentik/blueprints/brand.yaml
@@ -1,8 +1,11 @@
-# dezky branding for the default Authentik brand. branding_logo /
-# branding_default_flow_background are FILE-PATH fields (reject data URIs), so
-# the logo + carbon background are done via custom CSS (which allows data URIs).
-# The auth-flow title is set on the flow itself. ("Powered by authentik" footer
-# still needs the separate web-bundle patch.)
+# dezky branding for the default Authentik brand.
+# - branding_logo points at a dezky SVG injected into the server's web bundle
+# by the dezky-rebrand initContainer (see authentik/README "rebrand").
+# - colors (accent, button, carbon background) via custom CSS (CSS vars cross the
+# shadow boundary; selectors can't, so the logo had to be a real file).
+# - "Powered by authentik" -> "Powered by Dezky" is done by the same
+# initContainer sed'ing the bundle (the footer is light-DOM, unreachable by
+# brand CSS).
version: 1
metadata:
name: dezky-brand
@@ -15,6 +18,9 @@ entries:
domain: authentik-default
attrs:
branding_title: dezky
+ branding_logo: /static/dist/assets/icons/dezky-logo.svg
+ branding_favicon: /static/dist/assets/icons/dezky-favicon.svg
+ branding_default_flow_background: /static/dist/assets/icons/dezky-bg.svg
branding_custom_css: |
:root, :host {
--ak-accent: #D4FF3A;
@@ -23,19 +29,8 @@ entries:
--pf-global--link--Color: #D4FF3A;
--ak-flow-background: #0A0A0A !important;
}
- /* dezky logo in place of the authentik wordmark */
- .pf-c-brand, img.pf-c-brand, .ak-brand img, header img {
- content: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxODAiIGhlaWdodD0iNDgiIHZpZXdCb3g9IjAgMCAxODAgNDgiPjxyZWN0IHg9IjIiIHk9IjIiIHdpZHRoPSI0NCIgaGVpZ2h0PSI0NCIgcng9IjEzIiBmaWxsPSIjRjRGM0VFIi8+PHRleHQgeD0iMjQiIHk9IjM2IiBmb250LWZhbWlseT0iQXJpYWwsSGVsdmV0aWNhLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMzIiIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiMwQTBBMEEiIHRleHQtYW5jaG9yPSJtaWRkbGUiPmQ8L3RleHQ+PGNpcmNsZSBjeD0iMzgiIGN5PSIxMiIgcj0iNC41IiBmaWxsPSIjRDRGRjNBIi8+PHRleHQgeD0iNTgiIHk9IjM0IiBmb250LWZhbWlseT0iQXJpYWwsSGVsdmV0aWNhLHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMjciIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiNGNEYzRUUiPmRlemt5PC90ZXh0Pjwvc3ZnPg==") !important;
- height: 48px !important;
- width: auto !important;
- }
- /* carbon background — remove the authentik forest */
- .pf-c-background-image, .pf-c-background-image__src,
- ak-flow-executor, .ak-flow-card-background {
- background: #0A0A0A !important;
- }
- img.pf-c-background-image__src, .pf-c-background-image__src { display: none !important; }
- /* signal-green primary button + links */
+ .pf-c-background-image, .pf-c-background-image__src { background: #0A0A0A !important; }
+ img.pf-c-background-image__src { display: none !important; }
.pf-c-button.pf-m-primary,
.pf-v5-c-button.pf-m-primary,
.pf-v6-c-button.pf-m-primary {
diff --git a/infrastructure/production/fleet/authentik/server-rebrand.py b/infrastructure/production/fleet/authentik/server-rebrand.py
new file mode 100644
index 0000000..16245b8
--- /dev/null
+++ b/infrastructure/production/fleet/authentik/server-rebrand.py
@@ -0,0 +1,14 @@
+import sys,json
+d=json.load(sys.stdin); spec=d["spec"]["template"]["spec"]; img=spec["containers"][0]["image"]
+spec["volumes"]=[v for v in spec.get("volumes",[]) if v.get("name") not in ("webdist","webassets")]
+spec["volumes"]+=[{"name":"webdist","emptyDir":{}},{"name":"webassets","configMap":{"name":"authentik-web-assets"}}]
+script=("set -e\n cp -r /web/dist/. /wd/\n cp /assets/dezky-logo.svg /wd/assets/icons/dezky-logo.svg\n"
+ " cp /assets/dezky-bg.svg /wd/assets/icons/dezky-bg.svg\n cp /assets/dezky-favicon.svg /wd/assets/icons/dezky-favicon.svg\n"
+ " grep -rl 'Powered by authentik' /wd --include='*.js' | while read f; do sed -i 's/Powered by authentik/Powered by Dezky/g' \"$f\"; done\n echo rebrand-done")
+ic=[c for c in spec.get("initContainers",[]) if c.get("name")!="dezky-rebrand"]
+ic.append({"name":"dezky-rebrand","image":img,"command":["sh","-c",script],"volumeMounts":[{"name":"webdist","mountPath":"/wd"},{"name":"webassets","mountPath":"/assets"}]})
+spec["initContainers"]=ic
+c0=spec["containers"][0]
+c0["volumeMounts"]=[m for m in c0.get("volumeMounts",[]) if m.get("name")!="webdist"]
+c0["volumeMounts"].append({"name":"webdist","mountPath":"/web/dist"})
+json.dump(d,sys.stdout)
diff --git a/infrastructure/production/fleet/authentik/web-assets/dezky-bg.svg b/infrastructure/production/fleet/authentik/web-assets/dezky-bg.svg
new file mode 100644
index 0000000..3298edb
--- /dev/null
+++ b/infrastructure/production/fleet/authentik/web-assets/dezky-bg.svg
@@ -0,0 +1 @@
+
diff --git a/infrastructure/production/fleet/authentik/web-assets/dezky-favicon.svg b/infrastructure/production/fleet/authentik/web-assets/dezky-favicon.svg
new file mode 100644
index 0000000..65f2b68
--- /dev/null
+++ b/infrastructure/production/fleet/authentik/web-assets/dezky-favicon.svg
@@ -0,0 +1 @@
+
diff --git a/infrastructure/production/fleet/authentik/web-assets/dezky-logo.svg b/infrastructure/production/fleet/authentik/web-assets/dezky-logo.svg
new file mode 100644
index 0000000..2b52dd8
--- /dev/null
+++ b/infrastructure/production/fleet/authentik/web-assets/dezky-logo.svg
@@ -0,0 +1 @@
+