The live /usr/local/bin/lotus_deploy.sh (the `lotus-deploy` webhook target) was
never under version control and had rotted into two deploy-killing bugs that
froze chat.lotusguild.org on an old build:
1. CI gate: it waited on the WHOLE workflow run with a 15-min cap. Web CI shares
the single act_runner with the slow Tauri desktop builds, so a web run could
sit queued >15 min -> "result: timeout" -> deploy aborted. Now it gates only
on the "Build & Quality Checks" commit-status context (build + unit tests),
decoupled from "Trigger Desktop Build", and waits up to 45 min.
2. Dead element-call copy: `cp node_modules/@element-hq/element-call-embedded/...`
under `set -e` aborted every deploy after the widget was forked to
@lotusguild/element-call-embedded. The build already emits dist/public/
element-call; replaced the copy with a presence check.
Also: rsync now excludes config.json so the app deploy stops clobbering the
production runtime config (homeserver list / allowCustomHomeservers) that the
matrix repo owns. lxc106-cinny.sh now installs this script (syntax-checked).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Now that the client supports MSC3861 OIDC login, add mozilla.org to the
homeserverList and its origins to the CSP. mozilla delegates: homeserver ->
mozilla.modular.im, OIDC issuer -> chat.mozilla.org, identity -> vector.im.
- connect-src += mozilla.org mozilla.modular.im chat.mozilla.org vector.im
- img-src += mozilla.org mozilla.modular.im
Applied live to LXC 106 and synced here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Federated matrix.org users load avatars/images from their own media endpoint
(matrix-client.matrix.org), which img-src still blocked — so every avatar
tripped a CSP violation. Add https://matrix.org + https://*.matrix.org to
img-src to match connect-src. (media-src already allows https: so video/audio
were fine.) Applied live to LXC 106 and synced here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The chat.lotusguild.org nginx config (LXC 106) was edited directly on the box
and never tracked — which is how its CSP drifted (kept a dead Sentry URL and
blocked matrix.org logins). Snapshot it as cinny/nginx.conf (verbatim from prod,
incl. the corrected connect-src that now allows matrix.org/*.matrix.org) and
deploy it via lxc106-cinny.sh: back up the live file, swap, `nginx -t`, and
reload only on success (auto-restore the backup if validation fails, so a bad
config can't take the site down). TLS terminates at the NPM proxy, so this is a
plain HTTP server block with no secrets.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add matrix.org to homeserverList so federated friends with matrix.org accounts
can sign into chat.lotusguild.org. defaultHomeserver stays 0 (lotusguild), and
allowCustomHomeservers stays false — only the two listed servers are selectable,
so the client isn't opened up to arbitrary homeservers.
Deploys via lxc106-cinny.sh (cp -> /var/www/html/config.json); lotus-build.sh
preserves the live config across app rebuilds, so this is the authoritative copy.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tag the EC embed row and add a callout explaining the plan to fork
element-hq/element-call and self-build it for true ownership (decorations,
focus/screenshare, reconnect mic, theming, call-audio injection — all unfixable
against the prebuilt @element-hq/element-call-embedded bundle). Infra notes:
EC uses our LiveKit SFU (livekit/, LXC 151) + lk-jwt-service; a new build/deploy
pipeline will be needed. Full plan: LotusGuild/cinny → HANDOFF_ELEMENT_CALL_FORK.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- README: correct embedded Element Call version 0.19.3 -> 0.19.4 in the
Custom Features and Tech Stack tables
- landing/index.html: add a "Noise suppression" row to the Voice & Video
comparison table (Lotus = 3 tiers incl. on-device RNNoise ML) and note
the feature in the June 2026 narrative
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
99 curated APNG overlay frames stored in user Matrix profile (MSC4133),
visible to other Lotus Chat users in real time across timeline, members
list, and @mention autocomplete. Includes the Lotus Flower decoration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a fail-open Python sidecar (livekit/voice-limit-guard.py) that fronts
lk-jwt-service to enforce per-room voice participant caps for ALL Matrix
clients, not just Lotus Chat:
- lk-jwt-service moved to :8071 (systemd drop-in), guard owns :8070 so NPM's
existing /sfu/get + /get_token proxy targets are unchanged
- guard reads io.lotus.voice_limit.max_users (Synapse admin API, cached),
forwards to lk-jwt-service, and on an issued token decodes the LiveKit alias
+ requester, counts distinct Matrix users via LiveKit ListParticipants, and
returns 403 when the room is full (rejoins/extra devices allowed)
- any error fails open (returns upstream response) so calls never break
- systemd/voice-limit-guard.service; README documents ports, setup, revert
Also update landing page: voice limit is now server-enforced for all clients.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Feature description paragraph: added AFK auto-mute (1–30 min voice idle
timeout) and knock-to-join admin badge (live count on Members button)
- Comparison table: new AFK auto-mute row in Voice & Video section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New table row for 5 animated CSS wallpapers (rain, stars, grid pulse,
aurora, fireflies). Feature blurb updated to mention the animated
backgrounds and the glassmorphism body-background fix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds inline GIF preview and collapsible long messages rows to the
feature comparison table; extends the June 2026 feature list with all
five newly completed items (P3-5, P3-9, P5-19, P5-23, P5-26).
Includes pre-staged README additions for presence tracking, encrypted
search, privacy settings, draft persistence, and PiP persistence.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Update comparison table date to June 2026
- Add push-to-deafen (M key), night light filter, message length
counter, and TDS orange typing dots to also-available paragraph
- PTT row notes M = push-to-deafen
- Add Night Light row to UX & Extras comparison table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
README.md:
- Replaced the stale 'Auto-revert spotlight on screenshare' entry
(that 600ms revert-to-grid code was removed — it caused fullscreen
to show avatars instead of the screenshare)
- Added accurate entries for all four features added this cycle:
Screenshare fullscreen, PiP screenshare focus, Screenshare audio
mute, Custom status message
landing/index.html:
- Updated Lotus Fork feature description paragraph to mention
screenshare fullscreen, screenshare audio mute, PTT, and custom
status messages
- Added PTT row to Voice & Video comparison table
- Updated Screenshare row for Lotus Chat to note fullscreen + audio mute
- Added 'Custom status message' row to UX & Extras section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
LXC 139 NPM proxy host 49 now proxies both /sfu/get and /get_token
to lk-jwt-service (port 8070). Note that re-saving via NPM UI will
overwrite the conf and require re-adding the location blocks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Document the new member list presence indicators and per-member device
sessions panel with per-device SAS verification in both the landing
page feature list and the README custom features table.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The runner intermittently times out (5m) downloading the gitleaks binary
from GitHub. Add --retry 3 --retry-delay 5 --max-time 120 so transient
network blips don't fail the job.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add per-message read receipts comparison row (8-col, including new official
Cinny column from dc8f588). Update Lotus Chat feature description to include
per-message read receipt avatars and chat wallpaper in calls.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add chat.lotusguild.org as the featured Cinny fork column and cinny.in
as a second comparison column. Adds official Cinny card in Other
Clients section and updates table colspan to 8.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
OOM observed during rendering-chunks phase at 896MB and 3072MB.
6144MB heap with 8GB LXC memory is confirmed working.
Also update README rebuild command to match.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add !cancel command (anyone cancels own blackjack; PL50+ clears all room games)
- Add !wordlestats top-level command (wraps wordle stats function)
- Add !cleanwelcome admin command to purge stale welcome DM records
- !help now hides management section from sub-PL50 users, hides !health from non-admins
- !announce uses nio room cache for join_rule instead of an API call per room
- Fix _INVITEALL_BLOCKED comment (Commands is knock-gated, not restricted)
- welcome.py: skip duplicate DM if a pending welcome already exists for the user
- welcome.py: add clean_stale_dm_messages() helper
- welcome.py: replace no-op post_welcome_message with log_ready()
- bot.py: update import/call to match welcome.py rename
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Commands has join_rule=restricted so the join-rule filter didn't catch
it. Added _INVITEALL_BLOCKED set with the Commands room ID — any room
in that set is skipped regardless of join rule. Invite-only rooms
(Management, Cool Kids, Spam and Stuff) are still excluded by the
existing join_rule check.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
!setpl now iterates every Space room via the hierarchy API and updates
the power_levels state event in each. Setting a user to the room's
users_default cleans up the explicit entry rather than leaving a
stale PL0. Rooms where the bot lacks permission are counted and
reported but don't block the rest.
!inviteall skips rooms with join_rule=invite (Management, Cool Kids,
Spam and Stuff) — only public/restricted rooms get the invite. Also
skips rooms where the target is already a member.
_get_space_room_ids() fetches the Space child list via the v1 hierarchy
API with pagination support.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Five new commands, all gated behind is_elevated() (power level >= 50):
!mkroom <name> — clone #general's power levels, join rules, encryption,
history visibility, and avatar into a fresh v12 room,
auto-adds it to the Lotus Guild Space, and invites
the caller.
!roominfo — show room display name, ID, member count, join rule,
encryption status, and all users with PL > 0.
!topic [text] — set or clear the current room's topic.
!invite @user — invite any Matrix user to the current room.
!setpl @user <n> — update a user's power level (0-100); cannot exceed
the caller's own level.
Also adds urllib.parse.quote and MATRIX_HOMESERVER to imports, and
adds a "Management (PL50+)" section to !help.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add is_elevated() helper that reads the room power level from the nio
client store. Users at PL50+ (Nerdy Council and above) skip the cooldown
check entirely. The timestamp is still recorded so cooldown applies
if their power level is later reduced below 50.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each player now gets exactly one attempt per question. If their guess
is wrong it's recorded and they're locked out for that round. If both
players have guessed wrong the correct answer is revealed and the game
moves to the next question immediately (no need to wait for the 45s
timeout).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of a raw 6-word slice (which left dangling fragments like
'Partly - Not exclusively American; has'), extract the first complete
sentence (up to 10 words). Falls back to the 6-word cap only if no
sentence boundary is found in the response.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reduce word cap from 12 to 6 and add explicit instructions not to use
proper nouns, brand names, or place names in answers. Fixes the case
where the model blurted 'The Grand Canyon State (Arizona)' in response
to a geography question.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a rolling cache of the last 30 answers (persisted to
twentyq_cache.json) and pass the recent list to the model as an
explicit avoid clause. Also prompt the model to vary categories
each round. If the model still returns a cached answer it is
rejected and one retry is attempted automatically.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Allow open-ended questions like "What color is it?" or "How big is it?"
The model now gives brief descriptive answers (capped at 12 words) for
open questions while still answering Yes/No/Sometimes/Partly for binary
ones. Updated command descriptions and in-game prompts accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>