Files
Toju/emergency-fix/01-product-overview.md
T
myxelium e49b3ec112 chore: dev-stack switches, shared e2e harness, and desktop shell rules
- `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.
2026-08-14 03:19:29 +02:00

119 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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** | “Im 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 |
| “Im 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)
1. User registers on **Signal A** → home user id `H`.
2. User later needs **Signal B** → client is supposed to **auto-register/login** with a local **provision secret**, creating actor id `A_B` on B (username may be suffixed).
3. Peers on Signal B see the user as `A_B`, not `H`.
4. WebRTC peer map keys, `targetUserId` on relay, DM conversation ids, and call participant lists may mix `H` and `A_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:
1. Home register / login / logout / session restore
2. Silent foreign provision + Network settings badges
3. Discover / create / join / leave chat-servers (public, password, invite, moderated)
4. Text channels: send, edit, delete, react, typing, unread, sync after late join
5. Voice channels: join/leave, mute/deafen, speaking indicators, multi-device takeover, move between channels
6. Camera + screen share in voice / calls
7. DMs + friends + delivery states
8. Direct / group calls (ring, answer, decline, DND)
9. Attachments over data channel
10. Multi-device `account_sync` for saved rooms / chat batches / friends / avatar / emoji
11. 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 `identify` require 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) |