# Chat sign-in restyle and deployment report Repo implementation only; no production connection, deployment, or git commit was made. The server-owned `/opt/occ-element/config.json` is not in this repo. The supplied patch merges into that file rather than replacing its configuration. Signed-out visitors opening `chat.occupymedia.cc` will see a small Hub mark and wordmark in the top-left, a centered card containing **Sign in**, username and password fields, and a red submit button. Password recovery and account creation remain available as quiet links. Registration uses the same compact card. The background is `#060403`, the card is `#0F0A09`, and the font is system-ui. The mark is capped at 32px; there is no logo sidebar, background artwork, or hero. Long forms and errors scroll on small screens rather than overlapping the brand. Element's footer remains small and muted. ## Verified Element Web 1.12.30 behavior No Element source or config template was found in the repository or its installed dependencies. Research used the official **v1.12.30** source archive and the matching `ghcr.io/element-hq/element-web:v1.12.30` image, in temporary local files. | Configuration | Effect in 1.12.30 | | --- | --- | | `branding.auth_header_logo_url` | Image source in login/registration headers and the default welcome component. This is nested under `branding`, not a top-level key. | | `branding.logo_link_url` | Link destination where Element actually renders a linked logo, such as default welcome. | | `auth_header_logo_click_url` | Does not exist in this release. The login/registration header renders an unlinked image; a config option cannot make that header clickable. | | `brand` | App name used in translated text; does not change the header's layout or image. | | `branding.welcome_background_url` | Background artwork across auth pages. The injected CSS replaces the inline background with a solid color. | | `embedded_pages.login_for_welcome` | Uses the login form for the signed-out landing page, bypassing default/custom welcome content. | | `default_server_config` | Selects the homeserver and identity-server connection details; does not control branding or layout. Left intact. | | `disable_custom_urls` | Removes server editing. The CSS hides the redundant server label only when the edit control is absent. | | `disable_3pid_login` | Removes the username/email/phone selector, leaving username/password. | | `disable_login_language_selector` | Removes the language dropdown from the auth header. | See the pinned [configuration docs](https://github.com/element-hq/element-web/blob/v1.12.30/docs/config.md), [AuthHeaderLogo source](https://github.com/element-hq/element-web/blob/v1.12.30/apps/web/src/components/views/auth/AuthHeaderLogo.tsx), and [DefaultWelcome source](https://github.com/element-hq/element-web/blob/v1.12.30/apps/web/src/components/views/auth/DefaultWelcome.tsx). The structure is `.mx_AuthPage` → `.mx_AuthPage_modal` → `main.mx_AuthPage_modalContent`, with `.mx_AuthHeader` and `.mx_AuthBody` as siblings. The large logo comes from `.mx_AuthHeaderLogo > img` inside a 206px sidebar; the original body is 500px wide. The wrapper also renders a blur layer and `.mx_AuthFooter`. See [AuthPage](https://github.com/element-hq/element-web/blob/v1.12.30/apps/web/src/components/views/auth/AuthPage.tsx) and [upstream auth CSS](https://github.com/element-hq/element-web/tree/v1.12.30/apps/web/res/css/views/auth). There is no `.mx_AuthPage_logo` in this release's auth structure. `setting_defaults.custom_themes` accepts theme colors, fonts and Compound token overrides, not an arbitrary CSS stylesheet URL. `source_url` records a theme JSON's origin, not a CSS file. The injected link intentionally has no `data-mx-theme`, so Element's theme switching will not disable it. See the [theming docs](https://github.com/element-hq/element-web/blob/v1.12.30/docs/theming.md) and [theme loader/types](https://github.com/element-hq/element-web/blob/v1.12.30/apps/web/src/theme.ts). On first visits from iOS/Android, Element redirects a bare `/` to `/mobile_guide/` **before** loading config. Both chat vhosts redirect that app-download page to `/#/login`, which bypasses the mobile redirect. Disabling `mobile_guide_toast` also removes the later mobile-app prompt. See the pinned [startup source](https://github.com/element-hq/element-web/blob/v1.12.30/apps/web/src/vector/index.ts). ## Files changed | File | Purpose | | --- | --- | | [../edge/hub-chat-auth.css](../edge/hub-chat-auth.css) | Scoped auth overrides: corner branding, compact responsive card, warm colors, focus styles and red primary actions. Uses stable `mx_*` classes, structural selectors and Compound `data-kind`, with no hashed class selectors. | | [../edge/nginx.conf](../edge/nginx.conf) | Both HTTP tunnel and HTTPS chat vhosts serve `/hub-chat-auth.css` and inject its link once before `` in HTML. Clears upstream Accept-Encoding so sub_filter can read HTML. Routes the mobile guide to login. Matrix API and SSO helper keep their own locations. | | [../edge/docker-compose.edge.yml](../edge/docker-compose.edge.yml) | Read-only bind mount for the stylesheet. | | [auth-branding.patch.json](auth-branding.patch.json) | Hub logo URL, supported logo-link setting, direct login landing and removal of extra login selectors. | | [apply-auth-branding.py](apply-auth-branding.py) | Merge/preview utility. Preserves unrelated settings and writes in place to retain Docker's single-file bind mount. | | [../edge/README.md](../edge/README.md) | Links this report and corrects the documented chat routing. | | [AUTH-RESTYLE.md](AUTH-RESTYLE.md) | This research and deployment report. | The stylesheet is downloaded with the SPA shell; nginx cannot see hash routes. It applies only within `.mx_AuthPage`, including recovery/auth verification screens using that wrapper. Signed-in room UI keeps the existing custom theme. The `element.occupymedia.cc` alias vhost is not changed. ## Exact deployment steps (operator runs these; not executed here) Use the existing CT 150 checkout at `/opt/anarchoposter`. Run server commands with permission to edit `/opt/occ-element/config.json` and manage Docker. 1. **On the server, before copying files**, back up the config and edge files. Keep this shell open so `occ_auth_backup` remains available for later steps. ```bash occ_auth_backup="/opt/occ-element/backups/hub-auth-$(date -u +%Y%m%dT%H%M%SZ)" mkdir -p "$occ_auth_backup" cp -p /opt/occ-element/config.json "$occ_auth_backup/config.json" cp -p /opt/anarchoposter/infra/occupymedia/edge/nginx.conf "$occ_auth_backup/nginx.conf" cp -p /opt/anarchoposter/infra/occupymedia/edge/docker-compose.edge.yml "$occ_auth_backup/docker-compose.edge.yml" if test -f /opt/anarchoposter/infra/occupymedia/edge/hub-chat-auth.css; then cp -p /opt/anarchoposter/infra/occupymedia/edge/hub-chat-auth.css "$occ_auth_backup/hub-chat-auth.css" fi printf 'Backup: %s\n' "$occ_auth_backup" ``` 2. **From this workstation's repo root**, copy only the reviewed infra files. Set `OCC_EDGE_SSH` to your existing SSH destination for CT 150; no hostname or credentials are assumed here. ```bash OCC_EDGE_SSH='YOUR_EXISTING_CT_150_SSH_DESTINATION' rsync -aR -- \ infra/occupymedia/edge/nginx.conf \ infra/occupymedia/edge/docker-compose.edge.yml \ infra/occupymedia/edge/hub-chat-auth.css \ infra/occupymedia/edge/README.md \ infra/occupymedia/element/ \ "$OCC_EDGE_SSH:/opt/anarchoposter/" ``` 3. **On the server**, validate the candidate edge config with the new mounts, then merge the auth patch and recreate only the edge service: ```bash cd /opt/anarchoposter/infra/occupymedia/edge docker compose -f docker-compose.edge.yml config --quiet docker compose -f docker-compose.edge.yml run --rm --no-deps edge nginx -t python3 ../element/apply-auth-branding.py /opt/occ-element/config.json > "$occ_auth_backup/config.preview.json" diff -u "$occ_auth_backup/config.json" "$occ_auth_backup/config.preview.json" python3 ../element/apply-auth-branding.py /opt/occ-element/config.json --write docker compose -f docker-compose.edge.yml up -d --no-deps --force-recreate edge docker exec occ-edge-1 nginx -t ``` Run each command only if the preceding validation succeeded; `diff` exits 1 for the expected changes. Recreating the edge is required for the new CSS mount and ensures it sees copied files even if their inodes changed. Element needs no restart: the merge utility retains the config inode. Its existing `setting_defaults.custom_themes`, Compound tokens, `default_theme`, and `default_server_config` remain intact. Do not replace config.json with the patch JSON or try to register this CSS as a custom theme. 4. **On the server**, verify the public HTML, stylesheet and served config: ```bash curl -fsS --compressed https://chat.occupymedia.cc/ -o "$occ_auth_backup/index.served.html" curl -fsS 'https://chat.occupymedia.cc/hub-chat-auth.css?v=1' -o "$occ_auth_backup/auth.served.css" curl -fsS https://chat.occupymedia.cc/config.json -o "$occ_auth_backup/config.served.json" cmp hub-chat-auth.css "$occ_auth_backup/auth.served.css" python3 - "$occ_auth_backup" <<'PY' import json, sys from pathlib import Path backup = Path(sys.argv[1]) html = (backup / "index.served.html").read_text() assert html.count('href="/hub-chat-auth.css?v=1"') == 1 old = json.loads((backup / "config.json").read_text()) served = json.loads((backup / "config.served.json").read_text()) for key in ("setting_defaults", "default_theme", "default_server_config", "default_server_name"): assert served.get(key) == old.get(key), key assert served["branding"]["auth_header_logo_url"] == "https://app.occupymedia.cc/brand/logo.svg" assert served["branding"]["logo_link_url"] == "https://app.occupymedia.cc" assert served["embedded_pages"]["login_for_welcome"] is True assert served["disable_3pid_login"] is True assert served["disable_custom_urls"] is True print("Public HTML injection, stylesheet and preserved theme verified") PY ``` If a CDN serves an older HTML/config response, purge those URLs and retry. In a fresh signed-out browser, open `/`, `/#/login` and `/#/register`, and check the page on a phone as well. Check password errors, keyboard focus, recovery and an existing signed-in room. Browser reloads load the new config. Element also supports domain-specific config files; if this installation starts serving a successful `/config.chat.occupymedia.cc.json`, apply the same merge to its mounted source because it takes precedence over config.json. Rollback using the printed backup directory: restore the old nginx and compose files, restore the old config **in place**, then recreate the edge service with the old compose file: ```bash cp -p "$occ_auth_backup/nginx.conf" /opt/anarchoposter/infra/occupymedia/edge/nginx.conf cp -p "$occ_auth_backup/docker-compose.edge.yml" /opt/anarchoposter/infra/occupymedia/edge/docker-compose.edge.yml if test -f "$occ_auth_backup/hub-chat-auth.css"; then cp -p "$occ_auth_backup/hub-chat-auth.css" /opt/anarchoposter/infra/occupymedia/edge/hub-chat-auth.css fi python3 - "$occ_auth_backup/config.json" /opt/occ-element/config.json <<'PY' from pathlib import Path import sys data = Path(sys.argv[1]).read_bytes() with Path(sys.argv[2]).open("r+b") as target: target.write(data) target.truncate() PY cd /opt/anarchoposter/infra/occupymedia/edge docker compose -f docker-compose.edge.yml run --rm --no-deps edge nginx -t docker compose -f docker-compose.edge.yml up -d --no-deps --force-recreate edge ``` ## Local validation - Real Element v1.12.30 and nginx:1.27-alpine ran on an isolated local Docker network with temporary certificates and loopback ports; Matrix responses and the Hub asset request were mocked. No live accounts or production services were accessed. - Full repo nginx config passed `nginx -t`; compose configuration parsed. Nginx emits an existing duplicate `text/html` MIME warning in the unrelated files sign-in location; it does not prevent validation. - Both chat HTTP/HTTPS vhosts injected exactly one stylesheet link despite the client advertising gzip. JSON and the SSO helper were unchanged; CSS content and content type matched the repo file. - Chromium checked bare `/` → login, 420px desktop card, 32px corner logo, system-ui, warm background, red submit, two login inputs, mock failed login, visible errors, and registration. Checked widths 320/390/844px including landscape: no horizontal overflow or brand/form overlap. A fresh iPhone user agent reached the form through the mobile-guide redirect. - The injected link remained enabled after custom-theme loading. A room DOM without the auth wrapper retained its original button color. - Config merge checks confirmed preservation of theme, Compound tokens, homeserver and sibling keys, idempotence, and the original file inode. Real successful authentication and production cache/routing behavior still need the operator's post-deployment checks. Existing unrelated workspace changes were left alone; no files in `apps/api` were edited by this task.