- `LIVE_RELOAD=false npm run dev` keeps the renderer alive across a machine suspend; the reload client otherwise destroys the session under test. - `dev-peer.sh` plus a separate userdata dir runs a second local peer. - `tools/voice-probe.js` samples peer state and RTP counters from a live window, persisting to localStorage so a renderer reload cannot erase it. - e2e helpers for voice pairs, peer-role election, and a TURN relay. - Electron single-instance and dev-client-load decisions move into rules files with colocated specs.
6.3 KiB
01 — Product overview (user + system)
What the user thinks this app is
Toju / MetoYou is a Discord-like desktop chat app with:
- Accounts on a signal (signaling) server
- Chat-servers (communities) with text channels and voice channels
- Live text chat in those channels
- Voice / camera / screen share in voice channels
- Direct messages and private calls
- Friends, profile cards, custom emoji, GIFs, file attachments
- Multiple signal servers in the network settings (home + others)
The marketing promise implied by the product design:
Log in once. Join communities anywhere on the network. Chat and voice “just work.” Switching signal hosts should not feel like logging into a second product.
What the system actually is
| Layer | Role |
|---|---|
Angular client (toju-app/) |
All UX, NgRx state, domain logic, WebRTC + WebSocket clients |
Electron shell (electron/) |
Desktop window, SQLite persistence, IPC (window.api), provision-secret safeStorage, screen capture helpers |
Signaling server (server/) |
Auth tokens, public server directory REST, WebSocket identify/join/presence, RTC offer/answer/ICE relay, narrow chat/DM/voice_state fallbacks, multi-device account_sync. Does not store chat history. |
| Web / Capacitor | Same Angular app; weaker secret storage (sessionStorage); mobile voice/UI constraints |
Transport split (critical mental model)
| Transport | What the user experiences | What it actually carries |
|---|---|---|
| WebSocket to signal server | “I’m online / in this server / someone joined voice” | Identity, room membership, presence, SDP/ICE relay, chat_message + DM fallbacks, voice_state, account_sync |
| WebRTC media | Hearing / seeing people | Mic, camera, screen tracks (never through the signal server) |
| WebRTC ordered data channel | Messages syncing, files, emoji, many “live” features | Chat events, inventory sync, attachments, avatar/emoji chunks, voice/screen control, plugin bus |
If the socket is wrong or unauthenticated → user is invisible; no relay; chat fallback may fail.
If the data channel is dead → live text may still limp via chat_message, but history sync, attachments, emoji, many controls fail.
If media is routed wrong or peer never connects → voice UI lies (“In Voice”) with silence.
Core vocabulary (user ↔ engineering)
| User says | Engineering term |
|---|---|
| “My account / login” | Home session on homeSignalServerUrl + local profile in SQLite/IndexedDB |
| “Another network / signal host” | Foreign ServerEndpoint URL; needs per-URL credential |
| “A server / community” | Saved Room (chat-server) with sourceUrl / sourceId = which signal hosts it |
| “Text channel / voice channel” | Channels inside a room; text vs voice types |
| “I’m in voice” | Local VoiceSession + voice_state with isConnected + mic ownership via clientInstanceId |
| “DM / call someone” | Direct-message conversation + optional direct-call session |
| “It logged me out” | Often /login or /login?mode=authorize — may be foreign authorize, not true home logout |
Multi-signal identity (the footgun)
- User registers on Signal A → home user id
H. - User later needs Signal B → client is supposed to auto-register/login with a local provision secret, creating actor id
A_Bon B (username may be suffixed). - Peers on Signal B see the user as
A_B, notH. - WebRTC peer map keys,
targetUserIdon relay, DM conversation ids, and call participant lists may mixHandA_B.
Invariant the product claims: one human, one local profile, many per-server actor credentials — UX never asks for password again except last-resort authorize / real home expiry.
Invariant signaling claims: non-federated — peers in the same room must share the same signal endpoint to discover each other. Cross-room “same person” is a client-side identity problem.
Feature inventory (what “done product” includes)
Must work for a release-quality emergency fix:
- Home register / login / logout / session restore
- Silent foreign provision + Network settings badges
- Discover / create / join / leave chat-servers (public, password, invite, moderated)
- Text channels: send, edit, delete, react, typing, unread, sync after late join
- Voice channels: join/leave, mute/deafen, speaking indicators, multi-device takeover, move between channels
- Camera + screen share in voice / calls
- DMs + friends + delivery states
- Direct / group calls (ring, answer, decline, DND)
- Attachments over data channel
- Multi-device
account_syncfor saved rooms / chat batches / friends / avatar / emoji - Endpoint health, version compatibility, room signal affinity + fallback
Secondary (do not block P0 voice/auth/chat): plugins, game activity, themes, KLIPY, link previews, experimental media.
Platforms
| Platform | Notes for bugs |
|---|---|
| Electron desktop | Primary; provision secret in safeStorage; SQLite; best screen share |
| Browser | sessionStorage provision secret dies with tab; IndexedDB |
| Capacitor mobile | Auth routing, mic permissions, no reliable screen share; title bar hidden |
Trust boundaries (short)
- REST mutations and WS
identifyrequire bearer/session token per signal URL. - Actor user ids in request bodies are ignored server-side; token wins.
- Message bodies are not E2E encrypted beyond DTLS on WebRTC and TLS on WS.
- Signaling server is not the source of truth for chat history.
Where truth lives (for agents)
| Kind of truth | Prefer |
|---|---|
| Wire WebSocket types | agents-docs/features/signaling.md + server/src/websocket/handler.ts — not shared-kernel/signaling-contracts.ts |
| Auth multi-server | agents-docs/features/authentication.md + emergency-fix/04-*.md |
| Voice/WebRTC plumbing | toju-app/.../realtime/README.md — verify against code (see 10-code-lies) |
| Domain UX | toju-app/src/app/domains/<name>/README.md |
| Known past bugs | agents-docs/LESSONS.md — verify symbols still exist (some lessons describe desired fixes as if shipped) |