Replace the PROVIDERKEY placeholder with the actual Wiki.js OIDC strategy callback
(login/<strategy-key>/callback) so Authentik accepts the redirect back. Closes the
login loop for wiki.axion1337.chat.
The wiki-oidc-provider blueprint referenced !Env AUTHENTIK_WIKI_OIDC_CLIENT_ID/
SECRET which were never set, so the blueprint failed (status: error) and no
provider was created. client_id isn't a secret and the client_secret is read
into Wiki.js manually anyway, so drop both !Env refs and let Authentik generate
them. Read the values from the Authentik UI into Wiki.js. Guide 10 design fix.
The SOPS-encrypted secret existed locally but was untracked and not in the
kustomization, so Flux never applied it and both wiki pods stayed in
CreateContainerConfigError (secret not found). Add it to resources and commit.
Values not read; age recipient matches .sops.yaml so Flux can decrypt.
Wiki.js + a dedicated Postgres + public ingress for wiki.axion1337.chat, added to
the production kustomization so Flux applies it. Both pods intentionally wait on
the SOPS secret `wikijs-postgres-secret` (username/password) until sorb creates it
— same loud-but-visible pattern as the concierge bot. NetworkPolicy: Traefik ->
wikijs:http, wikijs -> wikijs-postgres:postgres (ingress-only, named container
ports). Ingress/Cert mirror the authentik pattern (letsencrypt-prod). Native OIDC
login (#0049, guide 10) and git-storage are configured post-start. All manifests
validated server-side (kubectl --dry-run=server).
How an Anwender/Admin reaches the suite wiki: public wiki.axion1337.chat, same URL
for both, Wiki.js's own OIDC login to Authentik (no forward-auth/outpost - that was
only for static Docusaurus). Ready-to-apply Ingress+Certificate manifest (authentik
pattern), the Authentik OAuth2 provider blueprint (parallel-safe slug wiki-js), and
the Wiki.js OIDC endpoints. Prepared, not deployed; supersedes guide 09 for the
suite (ADR-0014). Indexed in the guides README.
The 'Regelwerk Bindung resultiert in False' denial after login means the user
is not a member of wiki-zugang (step 2.4). Record it plus the redirect-loop and
outpost-offline cases so the next person recognises them fast.
A user hit the 'Neue Outpost-Integration' dialog, which only offers Docker/K8s
and can't be left empty. That is the service-connection creator, not the outpost.
Spell out: go to Applications -> Outposts -> Create (not Outpost Integrations),
and leave the Integration dropdown on 'No integration'.
Records the #0024 decision (axionwiki.lab) and flags it as a development-time
arrangement: the wiki still has to move into the ThreadNet Server Suite, and
surface alternatives beyond BookStack get re-examined afterwards. Expands the
Authentik outpost/token steps (version pinning, exact UI path, where the token
goes) and the Overmind/Traefik side (shared network, redirect-loop caveat, full
authResponseHeaders, request walk-through).
Docusaurus is static and has no auth of its own, so the gate goes in Traefik in
front of it via an Authentik proxy outpost. Guide 09 carries the complete,
ready-to-apply config: the Authentik blueprint (forward_single proxy provider +
application + wiki-zugang group + policy binding) and the Overmind side (proxy
outpost container + Traefik forwardAuth middleware). Deliberately kept as a guide,
not merged into the applied blueprint ConfigMap: nothing goes live until the
wiki hostname (#0024) is settled and the outpost token (a credential) is created
by sorb. Dedicated outpost on Overmind so per-request checks stay local and only
the login redirect crosses the VPN. Indexed in the guides README.
@apo could log in and message but no call would start — the click produced
zero server activity. Root cause: no Synapse profiles row, which makes every
displayname write 500 in _check_profile_size (NoneType), leaves the account
without a display name, and prevents the Element Call widget iframe from
initialising. Documents diagnosis (open_id_tokens=0 is the tell), the
cross-checked INSERT fix, and who is affected. Indexed in the troubleshooting
README.
The Authentik upstream provider used claims_imports.localpart.on_conflict:
add, which links a newly registered upstream identity to an EXISTING local
account when the derived localpart collides. Combined with Authentik's
case-sensitive username uniqueness, any holder of an invitation token could
register a case variant of an existing name and be linked into that account,
including service accounts (draupnir, alerts, maintenance-notify) that have
no upstream link at all.
Set on_conflict: fail so a colliding localpart aborts provisioning instead.
Existing links are unaffected.
Wrote 08-concierge-gaeste-einladungen.md without adding it to the guide index - the same class of oversight this whole session has been about. Fixed within the hour instead of finding it later by accident.
Said 7 themes, listed Gruvbox Dark and Wal by name - neither exists in element-values.yaml. Actual count verified against the live config: 17. Pointing at management/shared/branding.md as the single place that lists them with colours and light/dark labels instead of duplicating the list here, which is what let this drift in the first place.
Turns guest onboarding from an admin-only click in the Authentik UI into a traceable command a defined circle can run: !einladen creates a single-use invitation valid for three days, !verlaengern extends it twice at most, !freischalten makes it permanent, and expired accounts are deactivated automatically.
Authorisation is deliberately twofold - the Authentik group decides, the invite room makes it visible. A group alone leaves no trace of who invited whom; a room alone would authorise anyone who gets in.
Two deployment details matter: exactly one replica with Recreate, because a second instance would execute every command twice; and the script ConfigMap keeps its name hash so a change actually restarts the pod, avoiding the trap described in #50.
Endpoints and field names were taken from the running Authentik OpenAPI schema, not guessed. Refs axion1337.chat/axion1337.chat-gitops#48
Without bug_report_endpoint_url the whole section in Settings, Help and About is hidden, so a user has no way to obtain their logs at all. The element.io endpoint was deliberately removed on 2026-08-01 to stop logs flowing there.
The special value local brings the capability back without the outflow: the button reads Download logs, the dialog writes them to the device, and nothing is uploaded anywhere. Users can then attach them in Matrix, which is the path that is already in use.
Config only - no rebuild. Refs axion1337.chat/ThreadNet-Web#9
The anonymisation rewrite of 2026-08-07 gave every touched commit a new SHA, leaving the references in these documents pointing at objects that no longer exist. The mapping was reconstructed from the backup branches and each pair verified by tree and commit message before substituting.
Prefix lookups were built for lengths 7 to 12 and any ambiguous prefix would have been skipped; none were ambiguous across all 251 pairs.
The actual root cause of the failing blueprint, visible only after the KeyOf references were gone: the entry set recovery_flow alone, but the serializer validates the whole object and rejected it with 'When no user fields are selected, at least one source must be selected'.
All other fields are listed on purpose even though they match the model defaults - whatever the serializer does not receive is reset to its default, so a blueprint naming a single field can silently flip unrelated settings. Values are taken from the live database.
Refs axion1337.chat/axion1337.chat-gitops#60
Adding name and title was necessary but not sufficient - the blueprint kept failing. Running ak apply_blueprint by hand showed why the real error was never visible: when authentik logs a failed entry it calls str() on the KeyOf object, whose __repr__ resolves against an EMPTY blueprint and raises the very same exception. The logging path masks the actual validation error.
KeyOf also couples every stage binding to that one entry succeeding in the same run. Find resolves against the database instead, which is what the neighbouring blueprints in this same ConfigMap already do, and the flow exists.
Refs axion1337.chat/axion1337.chat-gitops#60
The flow entry only set designation. FlowSerializer requires name and title as well (neither has a model default), so validation failed, the entry never got a model instance, and every following KeyOf matrix_recovery_flow reference failed with it. The blueprint had status error on every run.
Corroborated inside the same ConfigMap: matrix-invitation-flow sets name and title and applies successfully.
Values are taken from the live database so nothing changes on the page users see. The typo in mail-adress is left as is - changing user-facing text would be a separate decision.
Closesaxion1337.chat/axion1337.chat-gitops#60
Dazu eine Aussage korrigiert, die seit heute nur noch halb stimmt: "2FA-Selbsteinrichtung optional (not_configured_action=skip)" gilt weiterhin fuer Mitglieder, aber nicht mehr fuer Admins.
Festgehalten sind vor allem die beiden Fallen: configure statt deny (deny sperrt Admins aus, ohne Weg zurueck ausser ueber den Cluster) und last_auth_threshold gegen die doppelte Abfrage. Dazu die wichtigste - stimmt der Gruppenname nicht, greift die Regel fuer niemanden und wirft dabei keinen Fehler.
Widget-Abhaengigkeit auf 0.19.2-threadnet.7. Umgesetzt ueber VITE_PRODUCT_NAME, ohne eine einzige geaenderte Quelldatei im Fork - upstream sieht die Variable an jeder Stelle vor.
Schliesst den dritten Punkt aus #54 ab.
Eigene Validate-Stage hinter der vorhandenen, gebunden an die Admin-Gruppe. Fasst KEIN Authentik-Standardobjekt an: not_configured_action haengt an der Stage statt an der Bindung, eine Umstellung am Standard haette alle Mitglieder getroffen.
not_configured_action=configure statt deny - wer keinen Faktor hat, wird beim Login durch die Einrichtung gefuehrt statt ausgesperrt. Bei einem Admin-Konto gaebe es nach einem deny keinen Weg zurueck ausser ueber den Cluster.
last_auth_threshold=hours=1 verhindert die doppelte Abfrage: die Standard-Stage auf Ordnung 30 validiert bereits, wer einen Faktor hat; diese hier ueberspringt sich dann.
BEWUSST auf einem Branch: die Gruppe ist mit dem Authentik-Standard "authentik Admins" angenommen und von sorb noch nicht bestaetigt. Trifft sie nicht zu, greift die Regel fuer niemanden - das waere schlimmer als keine Regel, weil es sich sicher anfuehlt.
Wichtigster Punkt: solange branding_logo hier steht, gewinnt der Blueprint. Wer das Logo in Authentiks Oberflaeche auswaehlt, sieht es bis zur naechsten Reconciliation - danach ist es wieder weg. Ein neues Logo gehoert in diese Zeile.
Dazu die Liste der oeffentlich erreichbaren Assets und der Hinweis, dass die vorhandene Wortmarke im wiki-Repo liegt und von Hetzner aus nicht erreichbar ist - sie muesste erst mit dem Client ausgeliefert werden.
Unser 512-px-PNG rendert in der Anmeldemaske viel zu gross: Authentiks Default ist ein SVG, das sich der Box anpasst, ein PNG dagegen nimmt seine Naturgroesse. sorb entwirft ein passendes Logo.
Wichtig: explizit auf den Default gesetzt, nicht die Zeile geloescht - ein Blueprint mit state: present setzt weggelassene Felder nicht zurueck, der alte Wert bliebe in der DB stehen. Titel, Favicon und Hintergrund bleiben.
Elements lake.jpg weicht dem Alpengluehen (John Towner, Unsplash License), Danksagung entsprechend umgeschrieben. Image rohana.axion1337.de/sorb/threadnet-web:v0.4.2 aus Pipeline 192.
Voraussetzung fuer den naechsten Schritt in gitops#54: die ThreadNet-Brand in Authentik referenziert genau diese Bilddatei und liefe vorher ins Leere.
branding_title/logo/favicon/default_flow_background auf dem bestehenden Brand-Eintrag (domain authentik-default) - kein zweiter Brand, sondern derselbe, um den die default_application schon haengt.
Die Assets werden von der Client-Auslieferung referenziert statt in Authentik hochgeladen: sie liegen dort ohnehin, und eine zweite Kopie ist genau die Quelle, aus der spaeter Abweichungen entstehen. Ein ConfigMap-Mount scheidet aus - die drei Dateien reissen base64-kodiert das 1-MiB-Limit.
BEWUSST auf einem Branch: alpenglow.jpg existiert erst nach dem naechsten Client-Deploy. Auf main gemergt wuerde Flux das sofort ausrollen und die Anmeldeseite haette einen 404 als Hintergrund. Reihenfolge: erst Client-Image, dann dieser Merge.
Ich hatte registration/passwordReset/deactivate auf false gesetzt mit der
Begruendung, der Web-Client zeige Schaltflaechen, die ins Leere fuehren.
Gemessen hatte ich nur zweierlei: dass UIFeature in der Config fehlt und dass
der Homeserver Registrierung ablehnt. Dass daraus sichtbare Schaltflaechen
folgen, habe ich NICHT geprueft - sorb hat auf /#/login nachgesehen, dort gibt
es keinen Registrieren-Button. Element blendet ihn im SSO-Flow ohnehin aus.
Die Aenderung loeste damit ueberwiegend ein Problem, das es nicht gab - und
deactivate: false nahm zusaetzlich die Konto-Loeschung aus den Einstellungen,
die vorher sichtbar war. Das hatte niemand verlangt.
Zurueck auf den Stand davor. Sollen einzelne dieser Schalter bewusst gesetzt
werden, ist das eine eigene Entscheidung mit eigener Begruendung.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
Der Web-Client zeigte Registrierung, Passwort-Reset und Konto-Loeschung an - alle
drei funktionieren auf dieser Instanz nicht:
- Registrierung ist am Homeserver abgeschaltet (gemessen: M_FORBIDDEN,
'Registration has been disabled. Only m.login.application_service
registrations are allowed.')
- Login laeuft ausschliesslich ueber SSO (Flows: m.login.sso, m.login.token) -
Passwoerter liegen in Authentik, nicht im Homeserver
- Konto-Loeschung laeuft ebenfalls ueber die Identitaetsverwaltung
Der Desktop-Client blendet die drei seit jeher aus; nur die Web-Config hatte
UIFeature gar nicht gesetzt. Damit sahen Web-Nutzer Angebote, die entweder in
einer Fehlermeldung enden oder am eigentlichen Weg vorbeifuehren - und
Registrierung widerspricht ausserdem der Vision-Entscheidung 'kontrolliert
wachsend, jeder Eintritt wird freigegeben'.
Chirurgisch eingefuegt, 17 Themes unberuehrt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
Nachtrag - der Bump war im vorigen Commit an einer falschen Einrueckung im
Suchtext gescheitert (8 statt 10 Zeichen, derselbe Fehler wie beim v0.4.0-Bump).
Diesmal zeilenbasiert statt ueber einen mehrzeiligen Suchtext.
Bringt: Tab-Titel ThreadNet, favicon.ico, Fehlerseite und Desktop-Hinweis mit
eigener Marke.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
Hebt Prod auf v0.4.1 (Tab-Titel, favicon.ico, Fehlerseite, Desktop-Hinweis).
Dazu logo_link_url: Elements Default zeigt auf element.io - ein Klick aufs Logo
haette also von unserer Anmeldemaske weggefuehrt. Zeigt jetzt auf die eigene
Instanz. Reine Config, wirkt sofort.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
Auf der Anmeldemaske stand weiterhin Elements gruenes Logo - meine Icon-Arbeit
deckte nur vector-icons/ ab, also Favicon, PWA und App-Icon. Das Logo IM Client
kommt aus einer anderen Quelle: AuthHeaderLogo.tsx und HomePage.tsx lesen
branding.auth_header_logo_url aus der Config und fallen sonst auf
themes/element/img/logos/element-logo.svg zurueck.
Das ist reine Konfiguration - kein Rebuild noetig, die ConfigMap reicht.
Als Ziel bewusst vector-icons/512.png statt einer neuen Datei: Die liegt bereits
ausgeliefert (verifiziert, HTTP 200) und stammt aus derselben Quelle wie alle
anderen Icons. Eine zweite Logo-Datei koennte davon abdriften - genau die Falle,
die in shared/branding.md steht.
Randbedingung, die die Wahl einschraenkt: webpack kopiert aus res/ nur themes/**
und vector-icons/** nach webapp. Ein Logo unter res/img/ waere gar nicht per URL
abrufbar.
Chirurgisch eingefuegt, YAML und eingebettetes JSON validiert, 17 Themes
unveraendert.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
Hebt das Image von v0.3.0 auf v0.4.0. Damit sind die zentrierten Icons, die
Markenfarbe #ed4f4c in der Browser-/PWA-Leiste und die About-Attribution
'ThreadNet — powered by Element' unter der Client-Version live.
Der Weg ist der in der ThreadNet-Web-CI dokumentierte: Tag pushen -> docker_web
baut und pusht rohana.axion1337.de/sorb/threadnet-web:v0.4.0 -> Tag-Bump hier
deployt es. docker_web lief in Pipeline 176 gruen (101 s).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
Die Ausnahme 'deploy-handover issues stay on the Gitea tracker' ist am
2026-08-02 entfallen - die Issues sind nach git.lab umgezogen, der Gitea-Tracker
ist leer. Damit stimmt jetzt auch der Satz weiter oben, dass kein Repo mehr auf
Gitea bearbeitet wird: Issues leben ohne Ausnahme auf git.lab, geschrieben wird
dort nur noch von der TURN-Rotation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
Schedule ist angelegt und zweimal probegelaufen; offen bleibt nur das
CANONIZE_TOKEN, jetzt mit Issue-Verweis (gitops#51) statt einer allgemeinen
Anleitung. Auch die beiden Probelaeufe sind vermerkt - Pipeline 159 hat die
fehlende Lab-CA aufgedeckt, 161 lief gruen durch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
Der Probelauf des neuen Jobs ist an genau der Stelle gescheitert, an der man es
nicht vermutet: 'git fetch gitea' lief durch (Gitea ist oeffentlich lesbar),
'git fetch origin main' dagegen brach ab mit 'unable to get local issuer
certificate'. Der Runner stellt die Lab-CA nur seinem eigenen Klon-Schritt
bereit, nicht dem Job-Script - der Klon oben im Log gelingt, der eigene
git-Aufruf zwei Zeilen spaeter nicht.
Zwei Aenderungen:
1. Die CA-Kette liegt jetzt als ci/lab-ca-chain.crt im Repo und wird per
GIT_SSL_CAINFO gesetzt (CI_SERVER_TLS_CA_FILE zuerst, falls ein spaeterer
Runner sie doch liefert). Ein CA-Zertifikat ist oeffentlich, kein Geheimnis -
dieselbe Loesung wie im wiki-bookstack-Repo. Verifiziert: die Kette
(aXionLabs Root + Intermediate) validiert https://git.lab.
2. 'git fetch origin main' faellt ganz weg. Der Runner hat main mit voller
Historie schon ausgecheckt, CI_COMMIT_SHA ist dessen Stand - ein Netzaufruf
weniger und eine Fehlerquelle weniger.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
Vorher brach der Job ohne CANONIZE_TOKEN sofort ab - auch dann, wenn gar keine
Rotation offen war. Das haette taeglich eine rote Pipeline erzeugt, bis das
Token existiert, und ausgerechnet die Gitea-Strecke ungetestet gelassen.
Jetzt laeuft der Job erst bis zur Feststellung 'nichts zu tun' durch (verifiziert
dabei Fetch und Erkennung) und verlangt das Token nur, wenn wirklich gepusht
werden muss. Rot wird die Pipeline dann, wenn es zaehlt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
Drei Dinge, die zusammengehoeren.
1. Falscher Satz raus. 'there is no direct-to-Gitea exception left' stand seit
eff643e (2026-08-02, von mir) achtzehn Zeilen ueber einem Absatz, der eine
laufende Ausnahme beschreibt - der Wiki-Umzug hatte die letzte Ausnahme auf
REPO-Ebene beseitigt, ich hatte das zu 'gar keine mehr' verallgemeinert.
2. Das Warum der Gitea-Kopie ergaenzt. Bisher stand nur der Mechanismus da
('the cluster pulls from Gitea'), nicht der Grund: git.lab haelt die
Bauplaene, Gitea eine Kopie, die der Cluster OHNE verfuegbares Lab erreicht.
Ohne diese Begruendung sieht der Aufbau nach Altbestand aus - eine spaetere
Session koennte die Flux-Quelle auf git.lab 'geradeziehen' und genau die
Lab-Unabhaengigkeit zerstoeren, fuer die sie da ist. Steht jetzt als
ausdrueckliche Warnung in beiden CLAUDE.md.
3. Den monatlichen Handgriff abgeschafft. Der Rotations-CronJob laeuft im
Cluster, erreicht git.lab nicht und pusht nach Gitea; von dort musste die
Rotation bisher per Hand ueber git.lab zurueck. Wird das vergessen,
ueberschreibt der naechste Mirror-Push sie und Flux spielt still das ALTE
Shared Secret wieder ein - ein Fehler ohne Symptom.
Der Schedule-Job canonize_rotation holt jetzt taeglich jeden
turn-secret-rotation-*-Branch von Gitea, der nicht in main steckt, merged
und pusht ueber git.lab. Taeglich statt monatlich zum Rotationstermin, weil
ein monatlicher Lauf genau einen Versuch haette.
Faellt etwas aus dem Rahmen - Merge-Konflikt oder ein Secret ohne ENC[ -,
bricht der Job ab und pusht NICHTS. Die rote Pipeline ist der Alarm; ein
zusaetzlicher Termin waere wieder ein Todo, das man vergessen kann.
Verifiziert: YAML parst, alle elf Script-Bloecke sind gueltige sh-Syntax, und
die Kernlogik gegen den echten Repo-Stand durchgespielt - beide vorhandenen
Rotations-Branches werden korrekt als 'schon in main' uebersprungen.
Noch offen (braucht Rechte, siehe Dateikopf): Project Access Token als
CANONIZE_TOKEN hinterlegen und den taeglichen Schedule anlegen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
'siehe Backlogs CFGMON-11' - das Repo heisst seit dem PM-Framework-Umbau
management (ADR-0005). Zu CFGMON-11 gibt es kein Issue, die Arbeit war beim
Umzug abgeschlossen; deshalb datiert statt verlinkt. Reiner Kommentar, die
Job-Definition ist unberuehrt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
Die Datei ist der Ort, an dem jemand Themes aendert - und der Ort, an dem nicht
stand, woher die Farben kommen. Genau so entstand die erfundene Fassung. Drei
Zeilen Kommentar mit Quelle, Verweis auf shared/branding.md und Hinweis auf die
zweite Kopie in ThreadNet-Web.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
Grundlage sind jetzt die Definitionen aus Anthropics theme-factory-Skill
(github.com/anthropics/skills/skills/theme-factory) statt meiner Interpretation
der Namen. Meine erste Fassung traf bei fast allen daneben - am deutlichsten bei
Sunset Boulevard, wo ich kraeftiges Koralle/Pink baute statt der vorgegebenen
Terrakotta-Palette #e76f51/#f4a261/#e9c46a/#264653.
Ob ein Theme hell oder dunkel gemeint ist, steht in den Beschreibungen teils
widerspruechlich ('Warm Sand - backgrounds' bei einem Theme, dessen Showcase-Seite
dunkel ist). Deshalb aus theme-showcase.pdf gemessen: sieben der zehn sind hell,
nur Sunset Boulevard, Golden Hour und Desert Rose dunkel. Vorher hatte ich sechs
faelschlich als dunkel angelegt.
Ableitung je Theme: die vier Originalfarben als Akzent/Sekundaer/Highlight/Text,
Flaechenabstufungen daraus gemischt, Username-Farben als Mischungen derselben
Palette - damit bleibt jedes Theme in sich stimmig.
Chirurgisch: nur die colors-Bloecke und is_dark der zehn Themes (299 Zeilen gegen
299), YAML validiert, die uebrigen sieben Themes unberuehrt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
Meine erste Auslegung des Namens waren kraeftige Sonnenuntergangstoene (Koralle,
Pink, Weinrot) - gemeint war die Hollywood-Hills-Richtung. sorbs eigene Wahl in
der BookStack-Oberflaeche macht das eindeutig: fuenf seiner sieben Farbwerte sind
der Coolors-Satz #264653/#2A9D8F/#E9C46A/#F4A261/#E76F51.
Genau der liegt jetzt auch dem Element-Theme zugrunde: Charcoal als Grundton,
Burnt Sienna als Akzent, Sandy Brown und Saffron als Sekundaerfarben, Persian
Green fuer Auswahl und Hervorhebung, warmes Beige als Text.
Chirurgisch: nur der colors-Block dieses einen Themes ersetzt (31 Zeilen gegen
31), YAML validiert, die uebrigen 16 Themes unberuehrt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
aXion1337 Light ist das exakte helle Gegenstueck zum Lieblingstheme: 'aXion1337
Dark' ist Gruvbox Dark (Farben und die acht Username-Farben sind die
Gruvbox-Palette), also ist Gruvbox Light das stilistische Analogon - gleiche
Rollenverteilung, gleiche Akzentfamilie, nur die Helligkeitsachse gespiegelt.
Dazu zehn benannte Paletten mit vollem Schluesselsatz (24 Farben inkl.
username-colors): Ocean Depths, Sunset Boulevard, Forest Canopy, Modern
Minimalist, Golden Hour, Arctic Frost, Desert Rose, Tech Innovation, Botanical
Garden, Midnight Galaxy - vier davon hell.
Chirurgisch eingefuegt (Lehre vom 2026-07-30): die YAML wurde nicht geparst und
nicht neu serialisiert, nur Text vor der schliessenden Array-Klammer ergaenzt.
Diff-Bilanz 0 entfernte / 427 neue Zeilen, YAML und eingebettetes JSON validiert.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj