--- domain: chat.irc.now status: live stack: gamja, nginx, soju (WebSocket) auth: OAuth2 (Keycloak SSO via SASL OAUTHBEARER) --- # irc.now Chat Web IRC client at `chat.irc.now`, powered by [gamja](https://git.sr.ht/~emersion/gamja) (AGPLv3). ## Architecture ``` browser --> chat.irc.now (Route, edge TLS) --> nginx container / --> gamja static files /socket --> proxy to soju-shared:8080 (soju WebSocket) ``` Single nginx container serving gamja's built static files and proxying `/socket` to soju's WebSocket listener. gamja's `config.json` points to `/socket`. ## Authentication OAuth2 SSO via Keycloak. The flow is: 1. gamja redirects to Keycloak (`auth.irc.now/realms/irc-now`) for login 2. User authenticates, Keycloak returns authorization code 3. gamja exchanges code for access token 4. gamja connects to soju via WebSocket using SASL OAUTHBEARER with the token 5. soju introspects the token against Keycloak and auto-creates the user ### Keycloak Clients - `chat` -- public client for gamja (no client secret). PKCE disabled (gamja doesn't support it). `exclude.session.state.from.auth.response` and `exclude.issuer.from.auth.response` set to avoid redirect_uri mismatch during token exchange. - `soju` -- confidential client for soju's token introspection. Credentials in `oidc-soju` secret. ### soju Config ``` auth oauth2 https://soju:@auth.irc.now/realms/irc-now enable-user-on-auth true ``` The soju-operator resolves the auth URL by reading `oidc-soju` secret and embedding client credentials in the URL. ## Theming irc.now design system applied via external CSS override (`theme.css`), injected at runtime by nginx `sub_filter` on the `` tag. Title renamed from "gamja IRC client" to "chat.irc.now" via a second `sub_filter`. No gamja source modification (AGPL compliance). ## Nginx Proxy `proxy_set_header Host $host;` is required so that soju's WebSocket Origin check passes (Origin `chat.irc.now` must match the Host header). ## Build ```bash oc start-build chat --from-dir=chat/ -n irc-josie-cloud --follow ``` Top-level `chat/` directory (not a Rust crate). Multi-stage Containerfile: node builds gamja from `git.sr.ht/~emersion/gamja`, nginx serves it. ## Deploy Manifests - `chat/deploy/buildconfig.yaml` -- ImageStream + BuildConfig - `chat/deploy/deployment.yaml` -- Deployment (port 8080) - `chat/deploy/service.yaml` -- ClusterIP Service - `chat/deploy/certificate.yaml` -- cert-manager Certificate (`chat-irc-now-tls`) - `chat/deploy/route.yaml` -- Route with edge TLS, 600s timeout for WebSocket ## Soju WebSocket soju-operator `Listener` type supports `websocket: bool`. The josie SojuBouncer CR has a WebSocket listener on `:8080` producing `listen ws+insecure://0.0.0.0:8080` in soju config. The soju-operator also supports `auth: Option` with `oauth2_url` and optional `secret_ref` for OAuth2 configuration.