Three classes of drift cleaned up across 85 .md files:
- Em-dashes: replaced ` — ` with the contextually correct separator
(colon for `term: explanation`, period to split clauses across
sentences/source lines, parentheses for asides, semicolon for
tight-linked continuations). Two intentional exceptions remain
in `docs/contributing/documentation.md` (the rule literal) and
inside a fenced ```sh block in `docs/contributing/actions/debugging/ci.md`.
- SPOT self-labels: rewrote "This is the SPOT for X" to describe
the subject directly ("This directory holds X" / "This file
describes X"). Cross-references to other artefacts' canonical
role remain unchanged.
- Heading-emoji position: moved leading-emoji headings (`# 🧩 Title`)
to trailing position (`# Title 🧩`) per the headline rule that
emojis MUST come after the title text.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.0 KiB
015 - KIX Service Management integration
User Story
As an operator running an Infinito.Nexus stack, I want KIX Start deployed as a first-class web-app-* role, fronted by web-app-oauth2-proxy so that every access to KIX is gated by Keycloak SSO with the realm's 2FA policy enforced, and so that helpdesk tickets share the same identity store, mail relay, dashboard, logout flow and proxy edge as every other service on the host.
Acceptance Criteria
- A new
roles/web-app-kix/exists and conforms to the role-meta layout from req-008, req-009, req-010 and req-011:meta/services.yml,meta/server.yml,meta/main.yml,meta/info.yml,vars/main.yml,tasks/main.yml(thin wrapper withrun_once_web_app_kixguard),tasks/01_core.yml,templates/compose.yml.j2,README.md. - The role declares its host-bound ports under
meta/services.yml.<entity>.ports.local.httpinside thelocal.httpband fromgroup_vars/all/08_networks.yml, picked viacli meta ports suggest. - The role declares a per-role docker subnet under
meta/server.yml.networks.local.subnet, picked viacli meta networks suggest. - KIX is reachable at
kix.{{ DOMAIN_PRIMARY }}over TLS throughsys-stk-front-proxyand emits HSTS. - An
web-app-oauth2-proxyinstance sits betweensys-stk-front-proxyand the KIX backend: every request tokix.{{ DOMAIN_PRIMARY }}that does not carry a valid OAuth2-proxy session cookie is redirected to Keycloak. The KIX backend is NOT reachable directly from the front proxy without traversing the OAuth2 proxy. - 2FA enforcement is realm-level: the Keycloak realm that backs the OAuth2 proxy has an OTP / WebAuthn flow configured, so a user without a second factor cannot complete the OAuth2 proxy redirect chain. The realm-level configuration is the single source of truth, so no 2FA logic lives in KIX or the role itself.
- LDAP is wired as KIX's user-directory backend against
svc-db-openldap(Auth::LDAP+Auth::Sync::LDAP): when a user authenticates against KIX their profile (display name, email, role group) is sourced from LDAP, so no manual KIX-side user pre-creation is required. - The
web-app-dashboardrole surfaces a card for KIX that links to its canonical URL, with the logo / title resolved via the standardlookup('config', 'web-app-kix', ...)path used by every other dashboard tile. - The universal logout endpoint terminates a KIX session like any other Infinito.Nexus app: the KIX session cookie is cleared and the browser lands on the project logout page.
- An end-to-end Playwright spec at
roles/web-app-kix/files/playwright.spec.jscovers the in-scope flow: TLS root + HSTS atkix.{{ DOMAIN_PRIMARY }}; anonymous request to the canonical URL redirects into the OAuth2-proxy / Keycloak chain (the realm-level 2FA step is exercised by the sharedweb-app-keycloakPlaywright suite, not duplicated per-app).
Deferred (KIX 18 OSS frontend limitation)
KIX 18 OSS exposes both Auth::HTTPBasicAuth (which reads
REMOTE_USER / HTTP_REMOTE_USER, semantically equivalent to
OTRS' Auth::HTTPHeaderModule) and a fully featured
Auth::LDAP + Auth::Sync::LDAP backend, so both are wired by
this role. The KIX 18 Angular frontend SPA, however, ships its own
login route (/auth?redirectUrl=…) and does not auto-submit
credentials based on the API-side Remote-User env. As a result
an authenticated oauth2-proxy session reaches the KIX UI and the
backend trusts the forwarded identity for API requests, but the
SPA still renders its own login form on first visit; the user
re-authenticates against the KIX login form against Auth::LDAP.
A truly transparent SPA-side SSO (login form skipped) requires a
small KIX frontend modification (wired to the existing
Auth::HTTPBasicAuth endpoint) and is tracked as a follow-on
iteration, not part of req-015.