T-HUB/infra/occupymedia/element/AUTH-RESTYLE.md
glass beadian b89dea5c93 feat: per-connection publish governance — auto, 1-admin, majority, consensus
Per the owner's spec: each social connection gets publishGovernance in its
fields — unset = manual publish (today), auto = dispatch on approval,
admin1/admin_majority/admin_consensus = a publish-phase chatgov poll
(subject draft_publish, its own open-poll slot, no collision with review
gates). Dispatch holds until the publish poll passes (E3 pre-dispatch
check); auto dispatches on approval. Includes migration 033, the PATCH
/connections/:id/publish-governance route (chatgov.manage), and 19+31 new
tests. Implementation by the branding-panel process (claude/opus), lint
and integration fixes here.
2026-10-14 08:04:56 +00:00

215 lines
13 KiB
Markdown

# 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 `</head>` 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.