Optional Element Call origin via config.json elementCallUrl (#43) #243
Merged
jared
merged 2 commits from 2026-09-27 02:24:48 -04:00
call-origin-split into lotus
No Reviewers
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
No labels
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: LotusGuild/cinny#243
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.
Part of #43: serve Element Call from its own origin (
call.chat.lotusguild.org).Nothing changes when this merges. The new path only turns on when
config.jsonsetselementCallUrl, which is the last step of the rollout below.Why
Today the call page is served from
chat.lotusguild.orgitself. A same-origin frame withallow-scripts allow-same-originhas full access to the app. Measured locally, the call frame today can:parent.documentOn its own origin, the same checks return
SecurityErrorfor the parent page and storage, and no service worker controls the frame. The sandbox flags stay the same:allow-same-originis harmless once the origins differ, and EC needs it for its own storage.What changes
callPageUrl.ts(+ tests): resolveselementCallUrl.localhost/127.0.0.1, for development).javascript:,data:, relative, plain http) falls back to the bundled page, so a typo can't break calls.CallEmbed: the widget URL comes from it. The widget origin used by the message guard (df395776) and by Capability Delegation follows automatically.io.lotus.inject_audioalso carries the clip's bytes, because the host'sblob:URL can't be fetched from another origin.Before merging
@lotusguild/element-call-embeddedto0.25.0-lotus.21once the element-call PR is merged and tagged. This branch still pins lotus.20.Test evidence
Local Synapse + LiveKit. The app runs on
127.0.0.1:5173; the call page is served from127.0.0.1:5174, which is cross-origin but same-site, likechat.vscall.chat.. The call-page server sends the proposed production headers, and the app document gets the proposedPermissions-Policy.parent.document/ applocalStorageNotAllowedError(hence the matrix PR's header change)df395776)set_layoutto the callframe-ancestors)Also: 1,268 unit tests pass, all 20 Playwright e2e tests pass, and
vite buildis clean.Side effects to expect after step 4
Known limits (not in scope)
frame-ancestorsbut not yet a full CSP, same as today's framed headers. A tighter CSP for EC is possible later but needs its own testing.Rollout order (nothing reaches users until step 4)
This work is split across three PRs, plus a final one-line config change. Each step is safe on its own, and none of steps 1–3 changes how calls work for anyone.
lotus-call-origin→ tagv0.25.0-lotus.21→ CI publishes the packagecall-origin-split, bump the pin to0.25.0-lotus.21, then merge (deploys)elementCallUrlisn't set.call-origin-split: install the two header snippets on LXC 106 by hand (nginx -t), then merge (deploysnginx.confwith its ownnginx -t+ rollback). Create the NPM proxy host. Verify with curl.cinny/config.json:"elementCallUrl": "https://call.chat.lotusguild.org/public/element-call/index.html"Rollback for step 4: remove that line.
config.jsondeploys on merge with no rebuild, and every new call is back on the bundled same-origin page. The same-origin copy atchat.lotusguild.org/public/element-call/stays in place for exactly this reason.Order matters: step 4 must come after step 2 ships lotus.21. I tested step 4 against the released lotus.20 locally. The call looks joined (the End button shows), but lotus.20 drops every message from the host, so host controls do nothing. Bob unmuted from the call bar, and Alice never saw "bob is speaking": he'd be silently muted.
Review checklist
resolveCallPageUrlcan't be made to load anything but an https page (tests coverjavascript:,data:, relative and http).isTauri()→ bundled page).targetOrigin = widget.origin; delegation uses the iframe's real origin).🤖 Generated with Claude Code
https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA