Files
cinny/LOTUS_REFERENCE.md
jaredandClaude Opus 5 b69099a862
CI / Build & Quality Checks (push) Successful in 1m31s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 8s
CI / Trigger Desktop Build (push) Successful in 5s
CI / Playwright smoke (e2e) (push) Successful in 3m59s
chore(calls): bump @lotusguild/element-call-embedded to 0.25.0-lotus.9
Brings speakingWhileMuted in io.lotus.call_state for the 'You're muted' nudge (#117).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
2026-09-19 23:17:21 -04:00

14 KiB

Lotus Chat — Engineering Reference

Repo: lotus branch at https://code.lotusguild.org/LotusGuild/cinny Deploy: push to lotus → CI → auto-deploy to chat.lotusguild.org (~11 min)

There is no backlog in this file. All open work lives in Gitea issues — cinny, element-call, cinny-desktop, matrix (infra/CI) — grouped by milestone (Features 2026-Q4, Desktop 2026-Q4, Manual QA backlog, Desktop QA backlog, the Audit 2026-09 · * set). Shipped features are documented in LOTUS_FEATURES.md; how to run the automated tests is in LOTUS_TESTING.md. The former LOTUS_TODO.md backlog was migrated to issues on 2026-09-17 (full history in git).

This file keeps only what a contributor needs to have open while working: the two design laws, decisions already made, what the server blocks, and the operational reference.


⚠️ TDS DESIGN LAW — READ BEFORE TOUCHING ANY UI

ALL Lotus Terminal Design System (TDS) styling — colors, animations, glows, borders, fonts, spacing — MUST come exclusively from /root/code/web_template/base.css CSS variables. Do NOT hardcode hex values. Do NOT invent new variable names. Canonical tokens: --lt-accent-orange, --lt-accent-cyan, --lt-accent-green, --lt-glow-*, --lt-box-glow-*, --lt-border-color, --lt-font-mono. Syntax-highlight token classes: .tok-kw .tok-str .tok-num .tok-cmt .tok-fn. Reference patterns: /root/code/tinker_tickets/ (markdown.js, base.js, ticket.css). Applies to every task without exception. New components must respect both TDS dark (LotusTerminalTheme) and TDS light (LotusTerminalLightTheme); non-TDS theme work uses vanilla-extract (match src/lotus-terminal.css.ts).

🧩 NATIVE-CINNY LAW — EVERY FEATURE MUST FEEL LIKE STOCK CINNY

Every feature must feel native to upstream Cinny — indistinguishable from what the Cinny team would ship. Reference: https://github.com/cinnyapp/cinny.

  • Use the folds design system, not bespoke UI (Button, Chip, IconButton, Menu, MenuItem, Dialog, Modal, Input, Switch, Badge, SettingTile, SequenceCard, …) and folds tokens (color.*, config.space.*, config.radii.*). Use folds Icon/Icons, never literal emoji, in UI chrome. No hardcoded hex/rgba(), no invented CSS variables.
  • Match Cinny's existing patterns — find the closest existing component/flow and mirror it before adding UI.
  • The ONE exception: explicit TDS features, which follow the TDS Design Law above (opt-in, only in Lotus Terminal mode).

Deferred / dropped (decided — kept for context)

  • [DEFERRED] P5-51 Federated "Identity Contexts" (session isolation) — multi-sprint, touches auth/crypto/storage core; smaller intermediate step = plain multi-account switch. [DROPPED] P5-52 per-room sync governor — js-sdk can't truly per-room filter /sync; only a cosmetic hide. [DEFERRED] P5-53 local scripting plugin — prefer a declarative automation-rules feature (no arbitrary code). [DEFERRED] Audit-3 profile banner — MSC4427 open/unmerged; revisit on merge. [WON'T FIX] P5-50 Windows HW media pipeline (WebRTC decode lives in WebView2; not injectable). [MOVED] P5-9 LFG → LotusBot !lfg.

🚫 Blocked Features (server / upstream gated)

Re-run /_matrix/client/versions + unstable_features after each Synapse upgrade. Re-checked on 1.157.1 (2026-07-23): no change — all four below are still false. The 1.156.0→1.157.1 delta unblocked nothing (it's a bugfix release; the only feature-bearing release in the gap was 1.156.0, which we were already running).

  • [BLOCKED] Live Location Sharing (MSC3489 + MSC3672 both false) — real-time GPS beacons over the existing static share.
  • [BLOCKED] Reaction/Relation Redaction (MSC3892 false) — remove a reaction without redacting the parent; current full-redaction fallback is acceptable.
  • [DONE 2026-07] Room Preview before joining (MSC3266) — the client was always built (JoinBeforeNavigateRoomCard via mx.getRoomSummary). The earlier "blocked" flag was a misdiagnosis: it tested /v1/rooms/{id}/summary (404), but the SDK calls the unstable im.nheko.summary/summary/{id} path, which returns 200 with name/topic/members/join_rule. Verified live after the 1.156 upgrade; also added a join-rule/encryption chip + Request-to-join for knock rooms to the preview card.
  • [BLOCKED] Thread Subscriptions (MSC4306 false) — "Follow thread" button (depends on the shipped Thread Panel).

📖 Reference

Server Capabilities (as of 2026-07)

  • Homeserver matrix.lotusguild.org · Synapse 1.157.1+trixie1 (upgraded 2026-07-23 from 1.156.0 — note the host was found on 1.156.0 while the docs claimed 1.155.0, so always verify with dpkg-query -W matrix-synapse-py3, don't trust the docs; apt package on Debian 13, LXC 151) · Matrix spec up to v1.12 (Synapse still advertises v1.12; MSC features via unstable_features).
  • MSC ON (re-dumped live from /_matrix/client/versions on 1.157.1): msc4140 · msc3771 · msc3440.stable · msc4133.stable · simplified_msc3575 · msc4222 · msc3266 (room summary live at unstable im.nheko.summary/summary/{id} — 200; the /v1/rooms/{id}/summary path is still 404) · msc3401_matrix_rtc · msc2285.stable · msc3827.stable · msc3981 · msc4380.stable · msc4445 · msc2659.stable · msc2666 · msc2432 · e2e_cross_signing · label_based_filtering. OFF/blocked: msc4306 · msc3882 · msc3912 · msc4155 · msc3489/msc3672 · msc3892 · msc4028 · msc4069 · msc4108 · msc3391 · msc4354 (sticky events — deliberately off, see the Matrix 2.0 section above) · msc4143 (RTC foci — not a gap: LiveKit is discovered via .well-known org.matrix.msc4143.rtc_foci, confirmed live, not this flag).
  • Dead client code: Synapse 1.157.0 removed msc3861 (MAS auth delegation) entirely — the ~6 msc3861/msc2965 references in src/ can never activate against this homeserver (we auth via Authelia oidc_providers). Harmless, but cleanup material.
  • Live endpoints: Report User (MSC4260) 200 · Report Room (MSC4151) .
  • Homeserver access (audits): Synapse = LXC 151 (pct exec 151 -- bash), config /etc/matrix-synapse/homeserver.yaml. Web deploy = LXC 106. Voice guard = voice-limit-guard.py on LXC 151.
  • SDK notes: no arbitrary profile-field methods (use mx.http.authedRequest() for MSC4133); js-sdk can't per-room filter /sync; sanitizer strips <math>/MathML; SW exists at src/sw.ts; getMatrixToRoom() builds invite URLs; EC audio-inject unblocked via the fork's io.lotus.inject_audio.

Key File Reference

What File Lines
Global keydown / room nav hooks/useKeyDown.ts · hooks/useRoomNavigate.ts whole / 19-72
Room unread counts atom state/room/roomToUnread.ts roomToUnreadAtom
Overlay portal provider pages/App.tsx · index.html 65 / 101
Room settings tabs features/room-settings/RoomSettings.tsx 27-56
State event read/write pattern features/common-settings/general/RoomEncryption.tsx 42-52
Power levels hooks/usePowerLevels.ts whole
Slash commands hooks/useCommands.ts 140-537
Chat background picker/defs features/settings/general/General.tsx · lotus/chatBackground.ts 945-981 / whole
Matrix.to URL builder plugins/matrix-to.ts getMatrixToRoom()
Media URL conversion utils/matrix.ts mxcUrlToHttp()
Search pagination / virtual features/message-search/{useMessageSearch,MessageSearch}.tsx 74-121 / 234-365
Call mic control plugins/call/CallControl.ts 206-212
Knock support check utils/matrix.ts 376-391
Notification mute push rules hooks/useRoomsNotificationPreferences.ts 110-150

Element Call fork — operational reference

Fork = LotusGuild/element-call (branch lotus, upstream base v0.25.0 since the 2026-09 sync — was v0.20.1); cinny consumes the npm package @lotusguild/element-call-embedded (built bundle copied into public/element-call/).

Toolchain (upstream-driven, accepted 2026-09): Node ≥ 22.13 (.node-version = 24) and pnpm 11, installed directly (npm i -g pnpm@<packageManager version>, currently 11.21.0) — not via corepack enable: matrix-js-sdk is a git dependency pnpm builds from source, and its own devEngines pins pnpm 11.9.0; a corepack-shimmed pnpm refuses to switch for that nested install and pnpm install fails (fork CI run #1854). pnpm 10 rejects the lockfile and Node 20 cannot build. Lint is oxlint + oxfmt (upstream dropped eslint/prettier in v0.25.0): pnpm lint (tsc + oxlint + knip) and pnpm format:check / pnpm format. matrix-js-sdk is pinned to a matrix-org/matrix-js-sdk#develop commit in the lockfile, as upstream ships it. Fork CI (.gitea/workflows/ci.yml) hard-gates lint + format + pnpm test:unit before build, with concurrency: cancel-in-progress.

Publish a new version (CI on tag push; needs the NPM_PUBLISH_TOKEN org secret): the published version is derived from the git tag — bump embedded/web/package.json (currently 0.25.0-lotus.9, published by CI; the secret is NPM_PUBLISH_TOKEN, names starting GITEA_ are reserved), push lotus, then git push lotus v0.25.0-lotus.1; the publish job builds and publishes to the Gitea registry. Always push (never delete) the annotated vX.Y.Z-lotus.N tag for every published version. Then in cinny bump the @lotusguild/element-call-embedded pin (currently 0.25.0-lotus.9) → npm install → build. Manual fallback: pnpm run build:embedded && cd embedded/web && npm version <ver> --no-git-tag-version && npm publish.

io.lotus.* widget actions (add new toWidget actions to the enum + LOTUS_TO_WIDGET_ACTIONS in src/lotus/lotusActions.ts; only send AFTER call-join or a 10s timeout fires):

Action Dir Purpose Module
io.lotus.call_state EC→host speaker/mute/camera stream (lotusCallState=1) lotusCallState.ts
io.lotus.focus_participant host→EC spotlight (works during screenshare) lotusFocus.ts
io.lotus.inject_audio host→EC soundboard clip mixed into call (lotusAudioInject=1) lotusAudioInject.ts
io.lotus.set_quality host→EC audio/screenshare bitrate/fps caps lotusQuality.ts
io.lotus.decorations host→EC in-call avatar decorations lotusDecorations.ts
io.lotus.set_deafen host→EC LiveKit-source deafen (P6-2) lotusDeafen.ts

Also flag-gated: lotusTransparent/lotusTheme, lotusDenoiseSource=1 (in-source ML denoise).

CI/CD + per-feature checklist

edit → commit → git push origin lotus
→ Gitea Actions (.gitea/workflows/ci.yml): npm ci → build + npm test + tsc + eslint + prettier (ALL hard gates) → audit + bundle-size (informational)
→ lotus_deploy.sh on LXC 106 polls the "Build & Quality Checks" status → npm ci && npm run build → rsync → live (~11 min)
   (a push that lands while a deploy is mid-build is queued and deployed right after — matrix@b6ea4a3; before that it was dropped)

Before marking a feature complete: npx tsc --noEmit (0 errors) · npx eslint src/ (0 new) · npx prettier --check src/ · npm test (Node runner via tsx, hard CI gate — colocated *.test.ts) · update README.md/landing/index.html for Lotus-custom features · visually verify on chat.lotusguild.org.

CI hardening (2026-07, reviewed):

  • Concurrencycancel-in-progress on cinny ci.yml and cinny-desktop release.yml (386a2979 / c5461ce): a superseded lotus push cancels its in-flight web CI and collapses queued ~30-min Tauri desktop builds to just the newest. Safe for deploys because lotus_deploy.sh now follows origin/lotus HEAD each poll iteration + resets to the gated SHA (matrix c15a489) — closes the latched-SHA freeze race.
  • Hard quality gates — typecheck/eslint/prettier promoted from continue-on-error to blocking (tree held clean). eslint gates on errors only; no-explicit-any warnings stay informational.

CI follow-ups (open):

CI follow-ups are tracked in the matrix repo: #8 dedicated desktop-linux runner, #9 debounce the desktop trigger, #10 verify Gitea concurrency. Build-once/deploy-the-artifact was considered and deferred (noted on #8).