Theming
Guacamole’s UI and KeyCloak’s login page (plus the KeyCloak realm’s display name) are branded from a single place: the afon-theme Maven module. Each deployment picks one theme via the AFON_THEME property; adding a new theme is purely a matter of adding files, no code changes.
Selecting a theme
Set AFON_THEME in afon.properties (see configuration):
AFON_THEME=emrys-hdrs
It defaults to emrys-hdrs if unset.
Layout
Every theme lives under afon-theme/src/main/resources/themes/<name>/:
themes/<name>/
guacamole/ # Guacamole extension — becomes a standalone jar at deploy time
guac-manifest.json
css/...
images/...
translations/...
keycloak/login/ # KeyCloak login theme, mounted at /opt/keycloak/themes/<name>
theme.properties
resources/css/...
resources/img/...
realm.properties # optional realm branding: displayName, displayNameHtml
Only guacamole/guac-manifest.json and keycloak/login/theme.properties are required — everything else is whatever files that theme needs; nothing is hardcoded by name or count. realm.properties is optional too: if a theme doesn’t define displayName/displayNameHtml, KeyCloak just falls back to showing the realm id.
Adding a new theme
- Create
afon-theme/src/main/resources/themes/<name>/guacamole/, following Guacamole’s branding extension format (aguac-manifest.jsonplus whatever css/images/translations it references). - Create
afon-theme/src/main/resources/themes/<name>/keycloak/login/, following KeyCloak’s theme format for thelogintype (atheme.propertiesplus whatever resources it references). - Optionally add
afon-theme/src/main/resources/themes/<name>/realm.propertieswithdisplayNameand/ordisplayNameHtml. - Set
AFON_THEME=<name>and deploy.
No changes to GuacamoleConfig, KeyCloakConfig, or KeyCloakProvider are needed.
How it’s wired
ThemeResources(afon-cdk) reads every file under a theme’sguacamole/orkeycloak/login/subtree off the synth classpath, whetherafon-themeis unpacked (IDE, tests) or packaged in a jar — including insideafon-cli.jar’sBOOT-INF/lib.GuacamoleConfigre-zips theguacamole/subtree into a standalone extension jar, shipped as a ConfigMapbinaryDataentry and copied intoGUACAMOLE_HOME/extensionsat pod start.KeyCloakConfigships thekeycloak/login/subtree as a ConfigMap, one entry per file (flattened into a valid ConfigMap key, reshaped back into the real path via the volume’sitems), mounted at/opt/keycloak/themes/<name>. Files that decode as valid UTF-8 (css, svg,theme.properties, …) ride indataso they stay readable viakubectl; anything else (images, fonts, …) rides inbinaryData.KeyCloakProvider#createRealmreadsrealm.properties(if present) to set the realm’sdisplayName/displayNameHtml, and setsloginThemeto the active theme name.
Verifying a theme without deploying
cdktf synth (via afon-cli.sh validate-stack <stack>, e.g. aws-full) generates the Terraform plan locally with no cloud credentials or live calls — useful to check a new theme’s ConfigMaps and volume mounts come out correctly before an actual deploy.