docs: P3-4 accessibility — features section, TODO/BUGS, LOTUS_TESTING §P
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+27
-26
@@ -15,32 +15,33 @@ step-by-step checks in [`LOTUS_TESTING.md`](./LOTUS_TESTING.md).
|
|||||||
|
|
||||||
Implemented and gate-green; confirm each per `LOTUS_TESTING.md`, then delete the row.
|
Implemented and gate-green; confirm each per `LOTUS_TESTING.md`, then delete the row.
|
||||||
|
|
||||||
| ID | Item | File / area | Test |
|
| ID | Item | File / area | Test |
|
||||||
| :--- | :------------------------------------------------------------------------------------- | :--------------------------------------------------- | :-------------------------------------------------------------------------------- |
|
| :--- | :-------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------- |
|
||||||
| #2 | Chat-background animation flicker (`contain:paint`) | `lotus/chatBackground.ts` | F1 |
|
| #2 | Chat-background animation flicker (`contain:paint`) | `lotus/chatBackground.ts` | F1 |
|
||||||
| #4 | Ringtone re-fixes: classic loudness + caller decline notice (A2 ✓ live) | `CallEmbedProvider.tsx`, `ringtones.ts` | A1,A3,A4 |
|
| #4 | Ringtone re-fixes: classic loudness + caller decline notice (A2 ✓ live) | `CallEmbedProvider.tsx`, `ringtones.ts` | A1,A3,A4 |
|
||||||
| #6 | Background vs. seasonal theme mutual exclusion | `state/settings.ts`, `General.tsx` | F2 |
|
| #6 | Background vs. seasonal theme mutual exclusion | `state/settings.ts`, `General.tsx` | F2 |
|
||||||
| #7 | Composer toolbar touch targets (≥44px) | `room/RoomInput.tsx` | E1 |
|
| #7 | Composer toolbar touch targets (≥44px) | `room/RoomInput.tsx` | E1 |
|
||||||
| #8 | Room Settings horizontal overflow (mobile) | `components/page/style.css.ts` | E2 |
|
| #8 | Room Settings horizontal overflow (mobile) | `components/page/style.css.ts` | E2 |
|
||||||
| #9 | Modal fullscreen on mobile (`useModalStyle`) | 22+ modal files | E3 |
|
| #9 | Modal fullscreen on mobile (`useModalStyle`) | 22+ modal files | E3 |
|
||||||
| #10 | Composer not hidden by keyboard (`100dvh`) | `src/index.css` | E4 |
|
| #10 | Composer not hidden by keyboard (`100dvh`) | `src/index.css` | E4 |
|
||||||
| #12 | PiP "All muted" badge re-fixed (was firing on any single mute) | `hooks/useCallSpeakers.ts` | G1 |
|
| #12 | PiP "All muted" badge re-fixed (was firing on any single mute) | `hooks/useCallSpeakers.ts` | G1 |
|
||||||
| N96 | Call-recovery overlay single "Back" button | `call/CallView.tsx` | A7 |
|
| N96 | Call-recovery overlay single "Back" button | `call/CallView.tsx` | A7 |
|
||||||
| N95 | AFK-monitor mic released on mute (OS indicator clears) | `hooks/useAfkAutoMute.ts` | L1 |
|
| N95 | AFK-monitor mic released on mute (OS indicator clears) | `hooks/useAfkAutoMute.ts` | L1 |
|
||||||
| N108 | Maskable PWA icons (Android adaptive) | `public/manifest.json` + `res/android/maskable-*` | L2 |
|
| N108 | Maskable PWA icons (Android adaptive) | `public/manifest.json` + `res/android/maskable-*` | L2 |
|
||||||
| EC | EC iframe load watchdog + self-heal + recovery UI | `plugins/call/CallEmbed.ts`, `CallView.tsx` | A7 |
|
| EC | EC iframe load watchdog + self-heal + recovery UI | `plugins/call/CallEmbed.ts`, `CallView.tsx` | A7 |
|
||||||
| N105 | Notification clicks work after tab close (SW `notificationclick` + `showNotification`) | `sw.ts`, `utils/dom.ts`, `ClientNonUIFeatures.tsx` | get a msg notif, close the tab, click it → app focuses/opens + routes to the room |
|
| N105 | Notification clicks work after tab close (SW `notificationclick` + `showNotification`) | `sw.ts`, `utils/dom.ts`, `ClientNonUIFeatures.tsx` | get a msg notif, close the tab, click it → app focuses/opens + routes to the room |
|
||||||
| Gal | MediaGallery lazy-decrypt (true virtualization deferred) | `room/MediaGallery.tsx` | H1 |
|
| Gal | MediaGallery lazy-decrypt (true virtualization deferred) | `room/MediaGallery.tsx` | H1 |
|
||||||
| a11y | aria-labels: edit-history / reaction / thread / reply | `message/*` (`FallbackContent`, `Reaction`, `Reply`) | I |
|
| a11y | aria-labels: edit-history / reaction / thread / reply | `message/*` (`FallbackContent`, `Reaction`, `Reply`) | I |
|
||||||
| P3-8 | Thread Panel (side drawer, chips, threaded receipts, thread composer) | `features/room/thread/*`, `RoomTimeline/RoomInput` | 6-step checklist in LOTUS_TODO §P3-8 |
|
| P3-8 | Thread Panel (side drawer, chips, threaded receipts, thread composer) | `features/room/thread/*`, `RoomTimeline/RoomInput` | 6-step checklist in LOTUS_TODO §P3-8 |
|
||||||
| P4-4 | KaTeX math (`$…$`, `$$…$$`, data-mx-maths; lazy chunk) | `utils/mathParse.ts`, `components/math/` | send `$x^2$`, `$$\int f$$`, `$5 and $10` (stays text), math inside code block (stays text) |
|
| P4-4 | KaTeX math (`$…$`, `$$…$$`, data-mx-maths; lazy chunk) | `utils/mathParse.ts`, `components/math/` | send `$x^2$`, `$$\int f$$`, `$5 and $10` (stays text), math inside code block (stays text) |
|
||||||
| P4-8 | Encrypted-search cache (opt-in toggle, clear button, logout wipe) | `utils/searchCache.ts`, message-search | enable in search panel → search → reload → coverage persists; logout wipes |
|
| P4-8 | Encrypted-search cache (opt-in toggle, clear button, logout wipe) | `utils/searchCache.ts`, message-search | enable in search panel → search → reload → coverage persists; logout wipes |
|
||||||
| N97a | Session blob migration + cross-tab logout sync | `state/sessions.ts`, `useSessionSync` | login on old build → new build migrates; logout in tab A → tab B drops to auth |
|
| N97a | Session blob migration + cross-tab logout sync | `state/sessions.ts`, `useSessionSync` | login on old build → new build migrates; logout in tab A → tab B drops to auth |
|
||||||
| P4-1 | Slack-style thread notifications (participating default, All/Mentions/Mute, badge math) | `utils/threadNotifications.ts`, `ClientNonUIFeatures`, `roomToUnread` | 6-step checklist in LOTUS_TODO §P4-1 |
|
| P4-1 | Slack-style thread notifications (participating default, All/Mentions/Mute, badge math) | `utils/threadNotifications.ts`, `ClientNonUIFeatures`, `roomToUnread` | 6-step checklist in LOTUS_TODO §P4-1 |
|
||||||
| AW-1 | Scheduled-message cancel no longer ghost-sends (error row on failure) | `ScheduledMessagesTray.tsx` | schedule → cancel with network cut → item stays + error; retry works |
|
| AW-1 | Scheduled-message cancel no longer ghost-sends (error row on failure) | `ScheduledMessagesTray.tsx` | schedule → cancel with network cut → item stays + error; retry works |
|
||||||
| AW-2 | Emoji lazy-load (search/autocomplete/recents fill in; board opens fast) | `plugins/emoji.ts` + consumers | first emoji-board open of a session: grid+search populate; reactions still label |
|
| AW-2 | Emoji lazy-load (search/autocomplete/recents fill in; board opens fast) | `plugins/emoji.ts` + consumers | first emoji-board open of a session: grid+search populate; reactions still label |
|
||||||
| AW-3 | SW precache (repeat-visit near-instant; deploys still picked up immediately) | `sw.ts`, `vite.config.js` | load app twice (2nd = cached assets); deploy → reload picks new version |
|
| AW-3 | SW precache (repeat-visit near-instant; deploys still picked up immediately) | `sw.ts`, `vite.config.js` | load app twice (2nd = cached assets); deploy → reload picks new version |
|
||||||
| AW-4 | Desktop CSP tighten + Escape/panel fixes + thread Jump to Latest | `tauri.conf.json`, Room/ThreadPanel | desktop: boots, avatars/media load, VT323 font renders, location maps embed, calls connect, deep links work |
|
| AW-4 | Desktop CSP tighten + Escape/panel fixes + thread Jump to Latest | `tauri.conf.json`, Room/ThreadPanel | desktop: boots, avatars/media load, VT323 font renders, location maps embed, calls connect, deep links work |
|
||||||
|
| P3-4 | Accessibility compliance pass (collapsed-msg SR sender, form/overlay labels, typing announce, focus-return, `?` help, jsx-a11y CI gate) | `message/*`, `RoomViewTyping`, `features/shortcuts/*`, `eslint.config.mjs` | LOTUS_TESTING §P — axe-core + VoiceOver/NVDA on the golden path |
|
||||||
|
|
||||||
**Verified working in live testing (2026-06):** A2, B1–B4, C1, C3, D (mic/camera/deafen/screenshare/fullscreen/more-menu/PiP). Denoise quality in D is still poor — tracked under the denoise project, not a regression.
|
**Verified working in live testing (2026-06):** A2, B1–B4, C1, C3, D (mic/camera/deafen/screenshare/fullscreen/more-menu/PiP). Denoise quality in D is still poor — tracked under the denoise project, not a regression.
|
||||||
|
|
||||||
|
|||||||
+29
-24
@@ -17,17 +17,17 @@
|
|||||||
|
|
||||||
From the matrix infra README (`/root/code/matrix/README.md`):
|
From the matrix infra README (`/root/code/matrix/README.md`):
|
||||||
|
|
||||||
| Thing | Value |
|
| Thing | Value |
|
||||||
|-------|-------|
|
| ------------------------ | ------------------------------------------------------------- |
|
||||||
| Synapse host | LXC **151**, `10.10.10.29` (Synapse 1.155.0) |
|
| Synapse host | LXC **151**, `10.10.10.29` (Synapse 1.155.0) |
|
||||||
| Synapse log | `/var/log/matrix-synapse/homeserver.log` |
|
| Synapse log | `/var/log/matrix-synapse/homeserver.log` |
|
||||||
| Synapse config | `/etc/matrix-synapse/homeserver.yaml` (+ `conf.d/`) |
|
| Synapse config | `/etc/matrix-synapse/homeserver.yaml` (+ `conf.d/`) |
|
||||||
| Synapse HTTP | `10.10.10.29:8008` |
|
| Synapse HTTP | `10.10.10.29:8008` |
|
||||||
| PostgreSQL host | LXC **109**, `10.10.10.44` (PG 17.9), db `synapse` |
|
| PostgreSQL host | LXC **109**, `10.10.10.44` (PG 17.9), db `synapse` |
|
||||||
| synapse-admin UI | `http://10.10.10.29:8080` |
|
| synapse-admin UI | `http://10.10.10.29:8080` |
|
||||||
| LiveKit / lk-jwt / guard | LXC 151: LiveKit `:7880/:7881`, guard `:8070`, lk-jwt `:8071` |
|
| LiveKit / lk-jwt / guard | LXC 151: LiveKit `:7880/:7881`, guard `:8070`, lk-jwt `:8071` |
|
||||||
| SSH path to Synapse | `ssh root@10.10.10.4` then `pct enter 151` |
|
| SSH path to Synapse | `ssh root@10.10.10.4` then `pct enter 151` |
|
||||||
| SSH path to PG | `ssh root@10.10.10.4` then `pct enter 109` |
|
| SSH path to PG | `ssh root@10.10.10.4` then `pct enter 109` |
|
||||||
|
|
||||||
**Getting a psql shell** (run on LXC 109, or from 151 over the network):
|
**Getting a psql shell** (run on LXC 109, or from 151 over the network):
|
||||||
|
|
||||||
@@ -50,10 +50,10 @@ temporarily raise it in `/etc/matrix-synapse/conf.d/log.yaml` (or the
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
loggers:
|
loggers:
|
||||||
synapse.rest.client.keys: { level: DEBUG }
|
synapse.rest.client.keys: { level: DEBUG }
|
||||||
synapse.handlers.e2e_keys: { level: DEBUG }
|
synapse.handlers.e2e_keys: { level: DEBUG }
|
||||||
synapse.storage.databases.main.end_to_end_keys: { level: DEBUG }
|
synapse.storage.databases.main.end_to_end_keys: { level: DEBUG }
|
||||||
synapse.handlers.devicemessage: { level: DEBUG } # to-device
|
synapse.handlers.devicemessage: { level: DEBUG } # to-device
|
||||||
```
|
```
|
||||||
|
|
||||||
Then `systemctl reload matrix-synapse` (reload re-reads log config without a
|
Then `systemctl reload matrix-synapse` (reload re-reads log config without a
|
||||||
@@ -86,6 +86,7 @@ use it as the primary client artifact and DevTools as the raw backup.
|
|||||||
/var/log/matrix-synapse/homeserver.log | grep "<user_id>"
|
/var/log/matrix-synapse/homeserver.log | grep "<user_id>"
|
||||||
```
|
```
|
||||||
- **Synapse SQL (LXC 109) — what the server thinks it holds:**
|
- **Synapse SQL (LXC 109) — what the server thinks it holds:**
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Current OTK inventory for the device (compare key_id set against the
|
-- Current OTK inventory for the device (compare key_id set against the
|
||||||
-- request body the client keeps retrying).
|
-- request body the client keeps retrying).
|
||||||
@@ -106,8 +107,10 @@ use it as the primary client artifact and DevTools as the raw backup.
|
|||||||
FROM e2e_fallback_keys_json
|
FROM e2e_fallback_keys_json
|
||||||
WHERE user_id = '@user:matrix.lotusguild.org' AND device_id = '<DEVICE_ID>';
|
WHERE user_id = '@user:matrix.lotusguild.org' AND device_id = '<DEVICE_ID>';
|
||||||
```
|
```
|
||||||
|
|
||||||
> Table names are Synapse 1.155 (`e2e_one_time_keys_json`,
|
> Table names are Synapse 1.155 (`e2e_one_time_keys_json`,
|
||||||
> `e2e_fallback_keys_json`). If a name is absent, list with `\dt e2e*` in psql.
|
> `e2e_fallback_keys_json`). If a name is absent, list with `\dt e2e*` in psql.
|
||||||
|
|
||||||
- **Confirms:** if the offending `key_id` (from the 400) is **present** in
|
- **Confirms:** if the offending `key_id` (from the 400) is **present** in
|
||||||
`e2e_one_time_keys_json` with a **different** stored value than the client's
|
`e2e_one_time_keys_json` with a **different** stored value than the client's
|
||||||
request body → OTK state has diverged (rust-crypto store vs Synapse). That is
|
request body → OTK state has diverged (rust-crypto store vs Synapse). That is
|
||||||
@@ -132,6 +135,7 @@ use it as the primary client artifact and DevTools as the raw backup.
|
|||||||
/var/log/matrix-synapse/homeserver.log | grep -E "<user_id>|<participant_id>"
|
/var/log/matrix-synapse/homeserver.log | grep -E "<user_id>|<participant_id>"
|
||||||
```
|
```
|
||||||
- **Synapse SQL (LXC 109) — undelivered / queued to-device events:**
|
- **Synapse SQL (LXC 109) — undelivered / queued to-device events:**
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- Backlog of to-device messages queued for the affected device. A growing
|
-- Backlog of to-device messages queued for the affected device. A growing
|
||||||
-- count here = the HS has the media-key events but the device isn't draining
|
-- count here = the HS has the media-key events but the device isn't draining
|
||||||
@@ -145,6 +149,7 @@ use it as the primary client artifact and DevTools as the raw backup.
|
|||||||
SELECT device_id, display_name, last_seen, ts
|
SELECT device_id, display_name, last_seen, ts
|
||||||
FROM devices WHERE user_id = '@user:matrix.lotusguild.org';
|
FROM devices WHERE user_id = '@user:matrix.lotusguild.org';
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Confirms:** to-device events present but undecryptable (client shows the
|
- **Confirms:** to-device events present but undecryptable (client shows the
|
||||||
`io.element.call.encryption_keys` "unexpected encrypted" warning) ⇒ there is
|
`io.element.call.encryption_keys` "unexpected encrypted" warning) ⇒ there is
|
||||||
**no valid Olm session** to decrypt them — the expected downstream of KE-1.
|
**no valid Olm session** to decrypt them — the expected downstream of KE-1.
|
||||||
@@ -265,7 +270,7 @@ Q4. KE-4: do delayed-event timeouts coincide with Synapse p99 latency spikes
|
|||||||
> "fix" before the planning session** — they are listed so evidence collection
|
> "fix" before the planning session** — they are listed so evidence collection
|
||||||
> can be paired with a recovery plan. Confirm the root condition (Q1/Q2) first.
|
> can be paired with a recovery plan. Confirm the root condition (Q1/Q2) first.
|
||||||
|
|
||||||
1. **Per-device logout + re-login of the affected device** *(lowest blast radius)*
|
1. **Per-device logout + re-login of the affected device** _(lowest blast radius)_
|
||||||
- **What:** log the one glitching device out and back in. Forces a fresh
|
- **What:** log the one glitching device out and back in. Forces a fresh
|
||||||
device id, fresh device keys, and a clean OTK batch — sidesteps a diverged
|
device id, fresh device keys, and a clean OTK batch — sidesteps a diverged
|
||||||
OTK store without touching other sessions.
|
OTK store without touching other sessions.
|
||||||
@@ -275,7 +280,7 @@ Q4. KE-4: do delayed-event timeouts coincide with Synapse p99 latency spikes
|
|||||||
- **Confirms/uses:** if KE-1 stops after this, OTK-store divergence (Q1) was
|
- **Confirms/uses:** if KE-1 stops after this, OTK-store divergence (Q1) was
|
||||||
the cause.
|
the cause.
|
||||||
|
|
||||||
2. **Client crypto-store reset (`clearLoginData` path)** *(medium)*
|
2. **Client crypto-store reset (`clearLoginData` path)** _(medium)_
|
||||||
- **What:** `clearLoginData()` in `src/client/initMatrix.ts` (coordinator's
|
- **What:** `clearLoginData()` in `src/client/initMatrix.ts` (coordinator's
|
||||||
file — do not edit) **deletes ALL IndexedDB databases** (incl.
|
file — do not edit) **deletes ALL IndexedDB databases** (incl.
|
||||||
`web-sync-store` and the rust-crypto store `crypto-store`), **unregisters
|
`web-sync-store` and the rust-crypto store `crypto-store`), **unregisters
|
||||||
@@ -294,16 +299,16 @@ Q4. KE-4: do delayed-event timeouts coincide with Synapse p99 latency spikes
|
|||||||
- **Use when:** the rust-crypto store itself is corrupt/diverged and option 1
|
- **Use when:** the rust-crypto store itself is corrupt/diverged and option 1
|
||||||
didn't clear it.
|
didn't clear it.
|
||||||
|
|
||||||
3. **SDK pin change off the RC** *(medium — codebase change, needs rebuild)*
|
3. **SDK pin change off the RC** _(medium — codebase change, needs rebuild)_
|
||||||
- **Current pin:** `package.json` → `"matrix-js-sdk": "41.6.0-rc.0"` (a
|
- **Current pin:** `package.json` → `"matrix-js-sdk": "41.6.0-rc.0"` (a
|
||||||
release candidate).
|
release candidate).
|
||||||
- **Finding (npm / GitHub changelog, checked 2026-07):** stable **`41.6.0`**
|
- **Finding (npm / GitHub changelog, checked 2026-07):** stable **`41.6.0`**
|
||||||
was released **2026-05-26**. Its only changelog line is *"Throw sane error
|
was released **2026-05-26**. Its only changelog line is _"Throw sane error
|
||||||
on completeLoginOnNewDevice IdP rejection"* — **no OTK / keys-upload / Olm /
|
on completeLoginOnNewDevice IdP rejection"_ — **no OTK / keys-upload / Olm /
|
||||||
to-device fix** relative to the RC. Later stable lines exist
|
to-device fix** relative to the RC. Later stable lines exist
|
||||||
(`41.7.0`, `41.8.0`; `41.7.0-rc.3` / `41.9.0-rc.0` seen as pre-releases).
|
(`41.7.0`, `41.8.0`; `41.7.0-rc.3` / `41.9.0-rc.0` seen as pre-releases).
|
||||||
Nearby crypto-relevant entries: `41.5.0` *"Enable encrypted history sharing
|
Nearby crypto-relevant entries: `41.5.0` _"Enable encrypted history sharing
|
||||||
by default"*; `41.4.0` key-backup handling. **No changelog entry directly
|
by default"_; `41.4.0` key-backup handling. **No changelog entry directly
|
||||||
addresses the KE-1 OTK-conflict symptom** in the immediate range — so
|
addresses the KE-1 OTK-conflict symptom** in the immediate range — so
|
||||||
moving RC→`41.6.0` stable is a low-risk hygiene step but is **not expected
|
moving RC→`41.6.0` stable is a low-risk hygiene step but is **not expected
|
||||||
to fix KE-1 by itself**. Before pinning, re-read the CHANGELOG for any
|
to fix KE-1 by itself**. Before pinning, re-read the CHANGELOG for any
|
||||||
@@ -312,7 +317,7 @@ Q4. KE-4: do delayed-event timeouts coincide with Synapse p99 latency spikes
|
|||||||
rust-crypto IndexedDB schema — a downgrade triggers the `IDB_VERSION_CONFLICT`
|
rust-crypto IndexedDB schema — a downgrade triggers the `IDB_VERSION_CONFLICT`
|
||||||
path in `initMatrix.ts`.
|
path in `initMatrix.ts`.
|
||||||
|
|
||||||
4. **Synapse-side OTK row surgery** *(LAST RESORT — highest danger)*
|
4. **Synapse-side OTK row surgery** _(LAST RESORT — highest danger)_
|
||||||
- **What:** deleting/rewriting rows in `e2e_one_time_keys_json` (and/or
|
- **What:** deleting/rewriting rows in `e2e_one_time_keys_json` (and/or
|
||||||
`e2e_fallback_keys_json`, `device_inbox`) for the affected device to force
|
`e2e_fallback_keys_json`, `device_inbox`) for the affected device to force
|
||||||
the client to re-upload a clean batch.
|
the client to re-upload a clean batch.
|
||||||
@@ -324,7 +329,7 @@ Q4. KE-4: do delayed-event timeouts coincide with Synapse p99 latency spikes
|
|||||||
- **Guardrails if ever done (planning session + HS owner only):** full
|
- **Guardrails if ever done (planning session + HS owner only):** full
|
||||||
`pg_dump` of `synapse` first; do it during **zero active calls**; delete only
|
`pg_dump` of `synapse` first; do it during **zero active calls**; delete only
|
||||||
the exact diverged `key_id` for the exact `device_id`; `systemctl restart
|
the exact diverged `key_id` for the exact `device_id`; `systemctl restart
|
||||||
matrix-synapse` to flush caches; then log the device out/in (option 1) so it
|
matrix-synapse` to flush caches; then log the device out/in (option 1) so it
|
||||||
republishes. **Never** run this speculatively.
|
republishes. **Never** run this speculatively.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -337,7 +342,7 @@ Do these **in order**. Aim to have client + server capturing the **same call**.
|
|||||||
`tail -F /var/log/matrix-synapse/homeserver.log | tee /tmp/lotus-call-$(date +%s).log`.
|
`tail -F /var/log/matrix-synapse/homeserver.log | tee /tmp/lotus-call-$(date +%s).log`.
|
||||||
(Optionally raise the `synapse.rest.client.keys` / `handlers.e2e_keys` /
|
(Optionally raise the `synapse.rest.client.keys` / `handlers.e2e_keys` /
|
||||||
`handlers.devicemessage` loggers to DEBUG per §0 and `systemctl reload
|
`handlers.devicemessage` loggers to DEBUG per §0 and `systemctl reload
|
||||||
matrix-synapse` — remember to revert after.)
|
matrix-synapse` — remember to revert after.)
|
||||||
2. **Prep client:** open Lotus Chat → Settings → Developer Tools → **enable
|
2. **Prep client:** open Lotus Chat → Settings → Developer Tools → **enable
|
||||||
Developer Tools** so the **Crypto Diagnostics** card is visible; note its
|
Developer Tools** so the **Crypto Diagnostics** card is visible; note its
|
||||||
entry count starts at (or reset by reload to) 0.
|
entry count starts at (or reset by reload to) 0.
|
||||||
@@ -373,7 +378,7 @@ Do these **in order**. Aim to have client + server capturing the **same call**.
|
|||||||
with pass-through wrappers (originals always called) that ring-buffer (max
|
with pass-through wrappers (originals always called) that ring-buffer (max
|
||||||
**200**) any line matching the KE signatures. No network, no timers.
|
**200**) any line matching the KE signatures. No network, no timers.
|
||||||
- `getCryptoDiagEntries()` — snapshot copy of the buffer (`{ ts, level, ke,
|
- `getCryptoDiagEntries()` — snapshot copy of the buffer (`{ ts, level, ke,
|
||||||
signature, message }`, most-recent-last).
|
signature, message }`, most-recent-last).
|
||||||
- `buildCryptoDiagReport(mx)` — JSON string: SDK version, device id, user id,
|
- `buildCryptoDiagReport(mx)` — JSON string: SDK version, device id, user id,
|
||||||
sync state, `cryptoReady` (`mx.getCrypto()` presence), per-KE counts, and the
|
sync state, `cryptoReady` (`mx.getCrypto()` presence), per-KE counts, and the
|
||||||
entry buffer. No tokens/PII beyond those ids; captured log lines are retained
|
entry buffer. No tokens/PII beyond those ids; captured log lines are retained
|
||||||
|
|||||||
@@ -1179,6 +1179,18 @@ Three one-tap presets at the top of **Settings → Notifications** that apply a
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Accessibility (P3-4)
|
||||||
|
|
||||||
|
WCAG 2.1 AA hardening of the golden path (find room → read → reply → send) for keyboard and screen-reader users.
|
||||||
|
|
||||||
|
- **Timeline for screen readers:** each message is `role="article"`; **collapsed messages announce their sender + time** (they drop the visible header, so AT would otherwise hear the body with no attribution). The timeline is a `role="log"` `aria-live="polite"` region so new messages are announced; emoji/emoticons carry text labels.
|
||||||
|
- **Live status:** typing indicators announce via a `role="status"` region; editing a message announces "Editing message from <sender>".
|
||||||
|
- **Forms & overlays:** all inputs have associated labels (visible `<label htmlFor>` or `aria-label`); the Media Gallery and Search overlays are named.
|
||||||
|
- **Focus management:** skip-to-content link + `nav`/`main` landmarks; genuine dialogs return focus to their trigger on close (inline popouts intentionally keep focus in context).
|
||||||
|
- **Keyboard-shortcuts help:** press <kbd>?</kbd> for a dialog of the existing shortcuts (Escape, type-to-focus composer, Enter/Shift+Enter send, message actions).
|
||||||
|
- **Regression gate:** a curated `eslint-plugin-jsx-a11y` rule set (ARIA correctness + label association) runs in CI. Files: `components/message/*`, `features/room/RoomViewTyping.tsx`, `features/shortcuts/*`, `utils/a11y.ts`, `eslint.config.mjs`.
|
||||||
|
- _Known limitation:_ list virtualization keeps far-scrolled history out of the a11y tree (perf trade-off); newly-arriving messages are announced.
|
||||||
|
|
||||||
## Infrastructure
|
## Infrastructure
|
||||||
|
|
||||||
### Authenticated Media
|
### Authenticated Media
|
||||||
|
|||||||
@@ -630,6 +630,38 @@ We shipped the diagnostics kit + a **Crypto Diagnostics** card (**Settings → D
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## P. Accessibility (P3-4) — needs a browser + a screen reader
|
||||||
|
|
||||||
|
The compliance fixes are gate-verified in code; these confirm the runtime a11y behavior only a human + AT can check. Tools: browser DevTools "axe" extension / Lighthouse a11y, plus **VoiceOver** (macOS ⌘F5) or **NVDA** (Windows).
|
||||||
|
|
||||||
|
### P1. Keyboard-only golden path (no mouse)
|
||||||
|
|
||||||
|
Tab from page load: **skip-to-content** link appears first (Enter jumps to the timeline). Tab reaches the room list (rooms are focusable, active room announced), open a room (Enter), type a character → focus lands in the composer, send with Enter (or Shift+Enter per your `enterForNewline` setting). No keyboard trap; visible focus ring throughout.
|
||||||
|
|
||||||
|
### P2. `?` shortcuts dialog
|
||||||
|
|
||||||
|
Press **?** (Shift+/) with focus NOT in a text field → the keyboard-shortcuts dialog opens, is focus-trapped, Escape closes it and focus returns to where you were. Pressing `?` while typing in the composer/search inserts a literal `?` (does NOT open the dialog).
|
||||||
|
|
||||||
|
### P3. Screen-reader: reading messages
|
||||||
|
|
||||||
|
With VoiceOver/NVDA on, arrow through the timeline: each message is announced as an article with **sender name + time** — critically, this includes **collapsed messages** (consecutive messages from the same person), which previously announced only the body with no sender. Reactions, "edited", replies, and delivery status are announced with labels.
|
||||||
|
|
||||||
|
### P4. Screen-reader: live announcements
|
||||||
|
|
||||||
|
- **New message** arrives while you're reading → announced (polite).
|
||||||
|
- **Someone starts typing** → "X is typing" announced once (not spammed per keystroke).
|
||||||
|
- **Editing a message** → the edit box announces "Editing message from X".
|
||||||
|
|
||||||
|
### P5. Focus return from dialogs
|
||||||
|
|
||||||
|
Open then close (Escape or ×): the **room topic viewer**, a **reaction viewer** (click a reaction count), and **Search** → focus returns to the button/element you opened them from (not lost to `<body>`). Inline popouts (emoji picker, autocomplete, hover menus) intentionally keep focus in context — that's expected, not a bug.
|
||||||
|
|
||||||
|
### P6. axe / Lighthouse scan
|
||||||
|
|
||||||
|
Run the axe DevTools extension (or Lighthouse → Accessibility) on a room view, Settings, and the login screen. Expect **no critical/serious** "missing accessible name" or "ARIA" violations on the golden path. Report any that appear (note: far-scrolled timeline history being virtualized out is a known, accepted limitation — not a finding).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Priority if you're short on time
|
## Priority if you're short on time
|
||||||
|
|
||||||
1. **O1 + O2** (threads + per-thread notifications) — the largest new surface; the main-timeline change is user-visible.
|
1. **O1 + O2** (threads + per-thread notifications) — the largest new surface; the main-timeline change is user-visible.
|
||||||
|
|||||||
+22
-12
@@ -141,7 +141,12 @@ Status: `[ ]` pending · `[~]` in progress · `[x]` completed
|
|||||||
|
|
||||||
## Priority 3 — Higher complexity / lower daily frequency
|
## Priority 3 — Higher complexity / lower daily frequency
|
||||||
|
|
||||||
### [ ] P3-4 · Accessibility Improvements (WCAG 2.1 AA)
|
### [~] P3-4 · Accessibility Improvements (WCAG 2.1 AA) — COMPLIANCE PASS DONE (2026-07), ⚠️ AWAITING LIVE AXE/SR AUDIT
|
||||||
|
|
||||||
|
**Shipped (compliance + shortcuts-help tier):** messages `role="article"` + collapsed-message sender/time announced to AT (the biggest gap — collapsed rows had no sender for a screen reader); ~10 unlabeled form inputs + Media Gallery / Search overlays named; emoji/emoticon aria-labels; typing indicator now announced via a `role="status"` live region; editing a message announces "Editing message from X"; focus now returns to the trigger on close of 4 genuine dialogs (RoomIntro/Reactions/RoomViewHeader-topic/Search — inline popouts correctly left); a `?` keyboard-shortcuts help dialog; and a **jsx-a11y lint gate** (curated ARIA-correctness + label rules, enforced in CI) to prevent regressions. Already-good before this pass: skip link + landmarks, timeline `role="log"`/`aria-live`, ~99% icon-button labels, labeled editor.
|
||||||
|
**DEFERRED (documented):** virtualization keeps scrolled-away history out of the a11y tree (architectural; the live-region announces newly-arriving messages) — not re-architected to avoid perf regression; roving-tabindex + command palette + section-jump shortcuts (user-deferred); the live axe-core / VoiceOver+NVDA audit → LOTUS_TESTING §P.
|
||||||
|
|
||||||
|
_Original scope (for reference):_
|
||||||
|
|
||||||
**What:** Comprehensive audit and fix pass targeting the critical user paths:
|
**What:** Comprehensive audit and fix pass targeting the critical user paths:
|
||||||
|
|
||||||
@@ -167,13 +172,14 @@ Status: `[ ]` pending · `[~]` in progress · `[x]` completed
|
|||||||
Built per the design below (4-agent build + 2-agent review). Gates green (tsc/eslint/build/tests). **Release note: threaded replies no longer render inline in the main timeline — roots show a "N replies" chip that opens the panel.**
|
Built per the design below (4-agent build + 2-agent review). Gates green (tsc/eslint/build/tests). **Release note: threaded replies no longer render inline in the main timeline — roots show a "N replies" chip that opens the panel.**
|
||||||
|
|
||||||
**Manual QA checklist (post-deploy):**
|
**Manual QA checklist (post-deploy):**
|
||||||
|
|
||||||
1. Reply in Thread (message menu) → panel opens; send text/upload/emoji into it (appears pending → confirmed)
|
1. Reply in Thread (message menu) → panel opens; send text/upload/emoji into it (appears pending → confirmed)
|
||||||
2. Reply to a reply inside the panel → event carries `m.thread` + `m.in_reply_to` with `is_falling_back:false`
|
2. Reply to a reply inside the panel → event carries `m.thread` + `m.in_reply_to` with `is_falling_back:false`
|
||||||
3. Main timeline: root + chip only (replies absent); chip count/time updates live; unread badge appears for others' thread replies and clears after viewing the panel
|
3. Main timeline: root + chip only (replies absent); chip count/time updates live; unread badge appears for others' thread replies and clears after viewing the panel
|
||||||
4. Room badge clears via normal markAsRead even with unread threads (unthreaded receipt)
|
4. Room badge clears via normal markAsRead even with unread threads (unthreaded receipt)
|
||||||
5. Reload: partitioning persists; encrypted-room threads decrypt (back-pagination too)
|
5. Reload: partitioning persists; encrypted-room threads decrypt (back-pagination too)
|
||||||
6. Escape / × closes; mobile = fullscreen panel; switching rooms and back restores the open thread
|
6. Escape / × closes; mobile = fullscreen panel; switching rooms and back restores the open thread
|
||||||
**What:** A right-side drawer for threaded conversations. Currently "Reply in Thread" exists but there is no panel to read or write thread replies.
|
**What:** A right-side drawer for threaded conversations. Currently "Reply in Thread" exists but there is no panel to read or write thread replies.
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
|
||||||
@@ -218,13 +224,14 @@ Features:
|
|||||||
**Shipped (Slack-style):** default = **Participating** (notified only for threads you've posted in or where you're @mentioned); per-thread override **All / Mentions-only / Mute** via the bell menu in the thread panel header; modes sync across devices (`io.lotus.thread_notifications` account data, pruned on write). Mute also suppresses the chip badge and subtracts the thread from the room's sidebar badge (client-side). Also fixed the underlying path: thread replies are notified via exactly one handler (room-level `ThreadEvent.NewReply`), with the main-timeline notifier + unread binder thread-guarded, and live badge refresh on `RoomEvent.UnreadNotifications`.
|
**Shipped (Slack-style):** default = **Participating** (notified only for threads you've posted in or where you're @mentioned); per-thread override **All / Mentions-only / Mute** via the bell menu in the thread panel header; modes sync across devices (`io.lotus.thread_notifications` account data, pruned on write). Mute also suppresses the chip badge and subtracts the thread from the room's sidebar badge (client-side). Also fixed the underlying path: thread replies are notified via exactly one handler (room-level `ThreadEvent.NewReply`), with the main-timeline notifier + unread binder thread-guarded, and live badge refresh on `RoomEvent.UnreadNotifications`.
|
||||||
|
|
||||||
**Manual QA checklist (post-deploy):**
|
**Manual QA checklist (post-deploy):**
|
||||||
|
|
||||||
1. Friend replies in a thread YOU posted in → notification + sound; in a thread you never touched → silent (chip badge only)
|
1. Friend replies in a thread YOU posted in → notification + sound; in a thread you never touched → silent (chip badge only)
|
||||||
2. @mention in any thread → notified regardless of participation
|
2. @mention in any thread → notified regardless of participation
|
||||||
3. Set a thread to Mute → no notifications, chip badge gone (bell-mute glyph), room sidebar badge drops by that thread's count
|
3. Set a thread to Mute → no notifications, chip badge gone (bell-mute glyph), room sidebar badge drops by that thread's count
|
||||||
4. Set to All → every reply notifies; Mentions-only → only @mentions
|
4. Set to All → every reply notifies; Mentions-only → only @mentions
|
||||||
5. Second device shows the same per-thread modes (account-data sync)
|
5. Second device shows the same per-thread modes (account-data sync)
|
||||||
6. Room-level Mute still silences everything incl. thread overrides
|
6. Room-level Mute still silences everything incl. thread overrides
|
||||||
**Known caveats:** Mentions-only can under-notify in E2EE rooms (decision runs pre-decryption — same class as the existing notifier); muted-thread badge subtraction is Lotus-only (other clients still count them).
|
**Known caveats:** Mentions-only can under-notify in E2EE rooms (decision runs pre-decryption — same class as the existing notifier); muted-thread badge subtraction is Lotus-only (other clients still count them).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -417,10 +424,11 @@ Features:
|
|||||||
**Decision:** Deferred (reviewed 2026-07). Multi-sprint, and it touches the auth/crypto/storage core — not worth the risk/effort for a niche need right now. Kept here with a concrete spec so it's actionable later.
|
**Decision:** Deferred (reviewed 2026-07). Multi-sprint, and it touches the auth/crypto/storage core — not worth the risk/effort for a niche need right now. Kept here with a concrete spec so it's actionable later.
|
||||||
|
|
||||||
**Future-work spec (why it's big):** the app is currently **single-session**.
|
**Future-work spec (why it's big):** the app is currently **single-session**.
|
||||||
|
|
||||||
- Session lives in `src/app/state/sessions.ts` under fixed localStorage keys — `cinny_access_token`, `cinny_device_id`, `cinny_user_id`, `cinny_hs_base_url`, plus the OIDC keys (`cinny_refresh_token`, `cinny_expires_at`, `cinny_oidc_*`).
|
- Session lives in `src/app/state/sessions.ts` under fixed localStorage keys — `cinny_access_token`, `cinny_device_id`, `cinny_user_id`, `cinny_hs_base_url`, plus the OIDC keys (`cinny_refresh_token`, `cinny_expires_at`, `cinny_oidc_*`).
|
||||||
- Persistence lives in `src/client/initMatrix.ts`: two fixed IndexedDB stores — `web-sync-store` (`IndexedDBStore`) and `crypto-store` (`IndexedDBCryptoStore`) — feeding one `createClient(...)`.
|
- Persistence lives in `src/client/initMatrix.ts`: two fixed IndexedDB stores — `web-sync-store` (`IndexedDBStore`) and `crypto-store` (`IndexedDBCryptoStore`) — feeding one `createClient(...)`.
|
||||||
|
|
||||||
True per-context isolation would require: (1) namespace every localStorage key per context (`ctx:<id>:cinny_*`); (2) per-context IndexedDB dbNames for **both** the sync store and the crypto store; (3) a context registry + switcher UI (create/rename/delete/switch); (4) full client teardown + re-init on switch (`initMatrix` currently assumes one global client); (5) per-context settings + notification/quiet-hours state; (6) careful crypto-store isolation so device keys never bleed across contexts. **Smaller intermediate step** if demand appears: plain multi-account (fast account switch) *without* the hard isolation boundary — much less risky, reuses most of the login flow.
|
True per-context isolation would require: (1) namespace every localStorage key per context (`ctx:<id>:cinny_*`); (2) per-context IndexedDB dbNames for **both** the sync store and the crypto store; (3) a context registry + switcher UI (create/rename/delete/switch); (4) full client teardown + re-init on switch (`initMatrix` currently assumes one global client); (5) per-context settings + notification/quiet-hours state; (6) careful crypto-store isolation so device keys never bleed across contexts. **Smaller intermediate step** if demand appears: plain multi-account (fast account switch) _without_ the hard isolation boundary — much less risky, reuses most of the login flow.
|
||||||
**Priority:** Extreme Low (Multi-sprint/Architectural).
|
**Priority:** Extreme Low (Multi-sprint/Architectural).
|
||||||
|
|
||||||
### [DROPPED] P5-52 · Desktop — Room-Level Sync Governor (Performance Control)
|
### [DROPPED] P5-52 · Desktop — Room-Level Sync Governor (Performance Control)
|
||||||
@@ -509,20 +517,22 @@ Exhaustive, low-level implementation details for backlog items. Follow these pat
|
|||||||
|
|
||||||
**Decisions (each backed by SDK evidence in node_modules/matrix-js-sdk):**
|
**Decisions (each backed by SDK evidence in node_modules/matrix-js-sdk):**
|
||||||
|
|
||||||
| Question | Decision |
|
| Question | Decision |
|
||||||
|---|---|
|
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| Thread rendering | **New lean `ThreadTimeline`** reusing `Message`, `useVirtualPaginator`, and RoomTimeline's exported timeline helpers (lines 156-227). Do NOT refactor 2214-line RoomTimeline (its ~35 hooks are hardwired to the room live timeline). |
|
| Thread rendering | **New lean `ThreadTimeline`** reusing `Message`, `useVirtualPaginator`, and RoomTimeline's exported timeline helpers (lines 156-227). Do NOT refactor 2214-line RoomTimeline (its ~35 hooks are hardwired to the room live timeline). |
|
||||||
| threadSupport | **Enable `threadSupport: true`** in `initMatrix.ts` (~line 39). ⚠️ Thread replies then LEAVE the main timeline (`room.js eventShouldLiveIn` → `shouldLiveInRoom:false`), retroactively on reload — MUST ship the "N replies" summary chip in the same release. Roots stay in both timelines. |
|
| threadSupport | **Enable `threadSupport: true`** in `initMatrix.ts` (~line 39). ⚠️ Thread replies then LEAVE the main timeline (`room.js eventShouldLiveIn` → `shouldLiveInRoom:false`), retroactively on reload — MUST ship the "N replies" summary chip in the same release. Roots stay in both timelines. |
|
||||||
| State | `roomIdToActiveThreadIdAtomFamily` (per-room, mirrors `roomIdToReplyDraftAtomFamily`) in new `state/room/thread.ts` + `getThreadDraftKey(roomId, threadRootId)` = `` `${roomId}::${threadRootId}` `` |
|
| State | `roomIdToActiveThreadIdAtomFamily` (per-room, mirrors `roomIdToReplyDraftAtomFamily`) in new `state/room/thread.ts` + `getThreadDraftKey(roomId, threadRootId)` = `` `${roomId}::${threadRootId}` `` |
|
||||||
| Composer | **Reuse RoomInput**: add optional `threadRootId` prop; scope its 3 atom-family lookups by draftKey (isolates thread drafts from the main composer); pass `threadRootId ?? null` at all 7 `mx.sendMessage/sendEvent` call sites — the SDK's `addThreadRelationIfNeeded` then emits spec-correct `m.thread` relations incl. reply-in-thread. Separate `useEditor()` instance in the panel. Hide schedule + commands in thread mode v1. |
|
| Composer | **Reuse RoomInput**: add optional `threadRootId` prop; scope its 3 atom-family lookups by draftKey (isolates thread drafts from the main composer); pass `threadRootId ?? null` at all 7 `mx.sendMessage/sendEvent` call sites — the SDK's `addThreadRelationIfNeeded` then emits spec-correct `m.thread` relations incl. reply-in-thread. Separate `useEditor()` instance in the panel. Hide schedule + commands in thread mode v1. |
|
||||||
| Unreads | v1 = unread badge on the summary chip (`room.getThreadUnreadNotificationCount` — counts already synced independent of threadSupport) + `markThreadAsRead` threaded receipt when panel open at bottom. |
|
| Unreads | v1 = unread badge on the summary chip (`room.getThreadUnreadNotificationCount` — counts already synced independent of threadSupport) + `markThreadAsRead` threaded receipt when panel open at bottom. |
|
||||||
| Mobile | Pure CSS like `MembersDrawer.css.ts`: fixed width toRem(360) desktop, `position:fixed; inset:0` under 750px. |
|
| Mobile | Pure CSS like `MembersDrawer.css.ts`: fixed width toRem(360) desktop, `position:fixed; inset:0` under 750px. |
|
||||||
|
|
||||||
**Critical side-effect fixes (one-liners, land FIRST):**
|
**Critical side-effect fixes (one-liners, land FIRST):**
|
||||||
|
|
||||||
1. `initMatrix.ts` → `threadSupport: true`.
|
1. `initMatrix.ts` → `threadSupport: true`.
|
||||||
2. `utils/notifications.ts:24` → `sendReadReceipt(latestEvent, type, /*unthreaded*/ true)` — otherwise markAsRead becomes `main`-scoped and room badges stick permanently unread (room unread total includes thread counts).
|
2. `utils/notifications.ts:24` → `sendReadReceipt(latestEvent, type, /*unthreaded*/ true)` — otherwise markAsRead becomes `main`-scoped and room badges stick permanently unread (room unread total includes thread counts).
|
||||||
|
|
||||||
**Known SDK traps (verified):**
|
**Known SDK traps (verified):**
|
||||||
|
|
||||||
- **Local echo gap:** chronological pending ordering means the thread timelineSet never receives pending events (`canContain` rejects; `room.getPendingEvents()` THROWS in this mode) — ThreadTimeline must render its own pending strip via `RoomEvent.LocalEchoUpdated` filtering on `threadRootId`, deduped against `thread.findEventById`.
|
- **Local echo gap:** chronological pending ordering means the thread timelineSet never receives pending events (`canContain` rejects; `room.getPendingEvents()` THROWS in this mode) — ThreadTimeline must render its own pending strip via `RoomEvent.LocalEchoUpdated` filtering on `threadRootId`, deduped against `thread.findEventById`.
|
||||||
- **Bootstrap:** `room.getThread(id) ?? room.createThread(id, room.findEventById(id), [], false)` — the SDK auto-fetches via `/relations` and inserts the root at top; gate rendering on `thread.initialEventsFetched`; decrypt with `decryptAllTimelineEvent` after init + each pagination.
|
- **Bootstrap:** `room.getThread(id) ?? room.createThread(id, room.findEventById(id), [], false)` — the SDK auto-fetches via `/relations` and inserts the root at top; gate rendering on `thread.initialEventsFetched`; decrypt with `decryptAllTimelineEvent` after init + each pagination.
|
||||||
- **Deep links:** `getEventTimeline(mainSet, threadEventId)` returns undefined for thread events — redirect jump-to-event to the panel (best-effort v1).
|
- **Deep links:** `getEventTimeline(mainSet, threadEventId)` returns undefined for thread events — redirect jump-to-event to the panel (best-effort v1).
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ experimental_features:
|
|||||||
msc3861:
|
msc3861:
|
||||||
enabled: true
|
enabled: true
|
||||||
issuer: http://localhost:8090/
|
issuer: http://localhost:8090/
|
||||||
client_id: "0000000000000000000SYNAPSE"
|
client_id: '0000000000000000000SYNAPSE'
|
||||||
client_auth_method: client_secret_basic
|
client_auth_method: client_secret_basic
|
||||||
client_secret: "REPLACE_WITH_A_SHARED_CLIENT_SECRET"
|
client_secret: 'REPLACE_WITH_A_SHARED_CLIENT_SECRET'
|
||||||
admin_token: "REPLACE_WITH_A_LONG_SHARED_ADMIN_TOKEN"
|
admin_token: 'REPLACE_WITH_A_LONG_SHARED_ADMIN_TOKEN'
|
||||||
account_management_url: "http://localhost:8090/account"
|
account_management_url: 'http://localhost:8090/account'
|
||||||
|
|
||||||
# With msc3861 enabled, Synapse disables its own password/SSO login and advertises
|
# With msc3861 enabled, Synapse disables its own password/SSO login and advertises
|
||||||
# `m.authentication` in /.well-known/matrix/client — which is exactly what the
|
# `m.authentication` in /.well-known/matrix/client — which is exactly what the
|
||||||
|
|||||||
Reference in New Issue
Block a user