[research] "Copy Link" should open in Lotus for guild members — matrix.to client hints vs direct chat.lotusguild.org permalinks #130
Closed
opened 2026-09-17 16:10:05 -04:00 by jared
·
2 comments
No Branch/Tag Specified
lotus
update-packages
sw-fix
read-me-update
image-path-changes
dm-calls
fix-2469
renovate/element-hq-element-call-embedded-0.x
renovate/npm-i18next-http-backend-vulnerability
renovate/npm-vite-vulnerability
dev
docs-update
more-theme
fix-257
imporve-thread-reply
revert-2402-improve-menu-congestion
mxidColor-toggle
update-sw-main-msg
v4.11.1
v4.10.5
v4.10.4
v4.10.3
v4.10.2
v4.10.1
v4.10.0
v4.9.1
v4.9.0
v4.8.1
v4.8.0
v4.7.1
v4.7.0
v4.6.0
v4.5.1
v4.5.0
v4.4.0
v4.3.2
v4.3.0
v4.2.3
v4.2.2
v4.2.1
v4.2.0
v4.1.0
v4.0.3
v4.0.0
v3.2.0
v3.1.0
v3.0.0
v2.2.6
v2.2.5
v2.2.4
v2.2.3
v2.2.2
v2.2.1
v2.2.0
v2.1.3
v2.1.2
v2.1.1
v2.1.0
v2.0.4
v2.0.3
v2.0.2
v2.0.1
v2.0.0
v1.8.2
v1.8.1
v1.8.0
v1.7.0
v1.6.1
v1.6.0
v1.5.1
v1.5.0
v1.4.0
v1.3.2
v1.3.1
v1.3.0
v1.2.1
v1.2.0
v1.1.0
v1.0.0
Labels
Clear labels
a11y
area: appearance
area: auth-session
area: build-ci
area: calls
area: desktop
area: media
area: messaging
area: mobile
area: moderation
area: navigation
area: notifications
area: settings
area: threads
bug
dependencies
docs
duplicate
enhancement
help wanted
invalid
needs-human-review
performance
planning
priority: critical
priority: high
priority: low
priority: medium
qa
question
research
security
tech-debt
ux
wontfix
Accessibility: keyboard, screen reader, contrast, motion
Client area: appearance
Client area: auth-session
Client area: build-ci
Client area: calls
Client area: desktop
Client area: media
Client area: messaging
Client area: mobile
Client area: moderation
Client area: navigation
Client area: notifications
Client area: settings
Client area: threads
Something is not working
Third-party package versions and advisories
README / LOTUS_* docs wrong or missing
This issue or pull request already exists
New feature
Need some help
Something is wrong
Re-render storms, leaks, heavy work on hot paths
Data loss, security hole, or crash on a main path
Broken feature or serious usability problem
Minor issue or polish
Wrong behaviour in an edge case or notable degradation
Manual QA: shipped, needs a human in a real environment
More information is needed
XSS, unsafe URLs, data leaks, auth/session
Code health, dead code, fragile patterns
Usability or visual inconsistency
This won't be fixed
Milestone
No items
No Milestone
Features 2026-Q4
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: LotusGuild/cinny#130
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Today
Message.tsx:400copiesgetMatrixToRoomEvent(...)— ahttps://matrix.to/#/…link. For guild members that lands on the matrix.to chooser page instead of opening Lotus.Research
client=/ "open in" memory, and whether custom (non-listed) clients can be registered)? Also thematrix:URI scheme (MSC2312) and whether the desktop app's existingmatrix:deep-link registration means amatrix:link is the better "opens in Lotus" form on desktop.https://chat.lotusguild.org/#/room/<roomId>/<eventId>?via=…— confirm the router already handles them (it should — the app navigates by hash) and what a non-member / other-homeserver user sees.RoomNavItem. Config-driven base URL (config.jsonhostBaseUrl) — no hardcoding.Research findings (2026-09-17)
What Lotus produces today
Four places copy a link, all through
src/app/plugins/matrix-to.tsand allhttps://matrix.to/#/…:Message.tsx:389-414)getMatrixToRoomEvent(roomId, eventId, via)https://matrix.to/#/!room:server/$event?via=…Space.tsx:124,SpaceTabs.tsx:137)getMatrixToRoom(idOrAlias, via)https://matrix.to/#/#alias:serverInviteUserPrompt.tsx:80)getMatrixToRoomLinks clicked inside Lotus never leave the app:
react-custom-html-parser.tsx:178-202parses matrix.to room/event links and navigates in place, and on Windows desktopuseDeepLinkNavigate.tsreceives OS-levelmatrix:URIs (MSC2312) forwarded by Tauri. So the problem is only the link a member pastes into Discord / SMS / email, which a browser opens on matrix.to.What matrix.to can and cannot do (read the source,
matrix-org/matrix.to@main)src/open/clients/Cinny.js, idcinny), so?client=cinnyis valid and matrix.to will show "Continue in Cinny"… but its deep link is hard-coded tohttps://app.cinny.in/home/<room>/<event>. There is no way to point it atchat.lotusguild.org.web-instance[<client>]=<host>exists but only Element honours it, and only against a hard-coded allowlist (trustedWebInstancesinElement.js: app.element.io, develop.element.io, chat.mozilla.org, chat.fedoraproject.org, chat.fosdem.org, webchat.kde.org, app.gitter.im, chat.blender.org).Cinny.jshasgetPreferredWebInstance()empty.?web-instance[cinny]=chat.lotusguild.orgis parsed (Link.jsdoesn't validate it) and then ignored.matrix:URI ("open in your default app"). Lotus desktop already registers that scheme on Windows, so desktop members can pick it — but it's a manual step on every device, and web/PWA users have no default handler.?via=a,b, while Lotus/Cinny's router reads?viaServers=a,b(paths.ts:48-51), so via servers from matrix.to → Cinny links are silently dropped today. (Upstream Cinny bug; irrelevant for joined rooms, matters for previews of unjoined federated rooms.)Upstream route: a PR to matrix.to adding
getPreferredWebInstance+ atrustedWebInstanceslist toCinny.js(mirroring Element's) withchat.lotusguild.orgin it would make?client=cinny&web-instance[cinny]=chat.lotusguild.orgwork. Mozilla/Fedora/KDE got in that way. Realistic but slow (matrix.to is lightly maintained), and it still only helps once the recipient clicks "Continue in Cinny"; matrix.to's remembered choice for most people is Element.Direct permalinks — verified working today
https://chat.lotusguild.org/home/<encoded room id or alias>/<encoded event id>?viaServers=a,bis already a fully functional deep link:curl -I https://chat.lotusguild.org/home/%21abc%3A…→ 200);hashRouter.enabledisfalseso no#/.setAfterLoginRedirectPath(Router.tsx:113-118) and the user lands in the room after login. Logged in: the route resolves; an unjoined room goes through the existingjoin-before-navigatepreview (RoomCard, knock/join).allowCustomHomeservers: true, so they can, but it is not what they expect from a "Matrix link").Recommendation
Copy two kinds of link, chosen by where it will be pasted, not one link that tries to be both:
?client=cinnyonly after the upstreamweb-instance[cinny]PR lands — until then the parameter just pushes people to app.cinny.in, which is worse than the picker. Keep matrix.to as the interoperable link; it is what the ecosystem expects and what other clients unfurl.https://chat.lotusguild.org/…permalink. Label it plainly; the tooltip says "Opens straight in Lotus Chat — for guild members". No setting, no auto-detection of the recipient (we can't know where it's going).getHomeRoomPath/getSpaceRoomPath+withSearchParam({ viaServers })) prefixed withgetOriginBaseUrl(), so it is correct for any deployment (dev, LXC 106, desktop) and for the hash-router config if it is ever turned on. ~60 lines: onegetLotusPermalink(room, eventId?)helper inplugins/matrix-to.ts(orutils/permalink.ts) + 3 menu items.react-custom-html-parser.tsx:178): achat.lotusguild.org/home/…link pasted back into a room should navigate in place, not open a new tab. That is the one piece of new parsing (parseLotusPermalink(href)matchinggetOriginBaseUrl()), and it should also feeduseDeepLinkNavigateso the desktop app handleshttps://chat.lotusguild.org/…opened from outside via Tauri's deep-link plugin (needs the https scheme registered intauri.conf.json— cinny-desktop follow-up, not required for v1).viavsviaServersmismatch by accepting both names inuseSearchParamsViaServers, so matrix.to → "Continue in Cinny" links carry their via servers. Also worth a one-line upstream PR to Cinny.Cinny.jsweb-instance allowlist withchat.lotusguild.org). Low effort, uncertain timeline; it would make option 1's?client=cinny&web-instance[cinny]=chat.lotusguild.orgthe best-of-both link.Nothing here touches
RoomTimelineor send paths; risk is confined to three menus and one link parser.Built and verified —
f528e5e4Facts from the raw sources (not summaries):
matrix.to/src/open/clients/Cinny.jshard-codeshttps://app.cinny.in/and emits?via=a,b;Element.jsis the only adapter honouringweb-instance[], against a hard-coded allowlist; upstream Cinnypaths.tsreadsviaServers. So matrix.to cannot be steered here, and the recommendation stands: keep matrix.to as the default link, add a direct one.Shipped
plugins/lotus-permalink.ts, unit-tested)./home/<room>for a joined room that lives under a space or in Direct now redirects to its real route instead of the preview card + "View" button. This is also the form matrix.to → "Continue in Cinny" produces, so those links improve too.?via=a,baccepted as an alias of?viaServers=(the matrix.to Cinny adapter's form).OidcCallbackreloads at the app root (window.location.replace(BASE_URL)), and the index loader then went straight to/home, ignoring the storedafter_login_redirect_urlthat the password flow honours. The index loader now consumes it via the sharedtakeAfterLoginPath(). This affected every guild member (SSO).Verified with Playwright on a local Synapse (space containing a 400-message room, both users joined):
/home/<room>/<event>→ login page → log in → lands on/<space>/<room>/<event>with the target message on screen.<base>/home/<room>/<event>?viaServers=localhost.Message: #Busy Room; clicking it jumps to the message in the same tab (no new page), URL carries the event id.<base>/<space>?viaServers=localhost.The OIDC redirect itself could not be exercised locally (no MAS); the change is a one-line reuse of the password flow's helper in the index route, covered by reading both paths.
Not done / optional follow-ups: Share Room dialog and its QR still use matrix.to only (by design — a phone camera's Matrix app expects it); desktop app does not register
https://chat.lotusguild.orgas a deep-link scheme (Tauri deep-link plugin config in cinny-desktop; web handling is already there); upstream PR to matrix.to addinggetPreferredWebInstance+ allowlist toCinny.js— worth opening if you want?client=cinny&web-instance[cinny]=chat.lotusguild.orgto work one day.