lotus.21 checks widget messages against the host's origin (works same- and
cross-origin) and accepts soundboard clip bytes. Same-origin behaviour is
unchanged; verified against the published package: join, both screenshare
paths, PTT/deafen in the frame, layout/reactions/settings, speaking and mic
level, soundboard, avatars, muted-speech warning, per-person volume, and the
foreign-frame spoof stays blocked.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
Groundwork for serving the call page from its own origin
(call.chat.lotusguild.org). Inert until config.json sets `elementCallUrl`:
without it the bundled same-origin page is used exactly as today.
- callPageUrl: resolves `elementCallUrl` — absolute https only (http only on
localhost for development); anything else, and the desktop app, fall back
to the bundled page so a bad value can't break calls. Set once from the
loaded client config.
- CallEmbed builds the widget URL from it; the widget origin (used by the
message guard and Capability Delegation) follows automatically.
- Soundboard: a host blob: URL can't be fetched from another origin, so
io.lotus.inject_audio now also carries the clip's bytes (`audio`). Forks
that predate it ignore the field and use `url`, so this is safe on the
released fork.
Needs element-call's lotus-call-origin branch (host-origin message check +
inject_audio bytes) released and pinned before `elementCallUrl` is set.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA