Files
Toju/emergency-fix/03-architecture-map.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

155 lines
4.9 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.
# 03 — Architecture map (for agents)
## Bounded contexts (product client)
See `toju-app/src/app/domains/README.md`. Emergency-relevant domains:
| Domain | Owns |
|--------|------|
| `authentication` | Login/register HTTP, provision secret, per-URL credentials, authorize navigation |
| `server-directory` | Endpoints, health, discovery, invites, room metadata affinity |
| `chat` | Message rules, sync rules, chat UI |
| `direct-message` | DMs, friends, offline queue, PeerDelivery usage |
| `direct-call` | Private call sessions / rings |
| `voice-session` | Join/leave bookkeeping, floating controls, settings storage |
| `voice-connection` | Facade over realtime for mic/camera/playback/VAD |
| `screen-share` | Picker / quality |
| `attachment` | Chunked P2P files |
| `access-control` | Permissions / bans |
**Infrastructure (not a domain):** `infrastructure/realtime/` (WebRTC + signaling), `infrastructure/persistence/`, `infrastructure/mobile/`.
**Global NgRx:** `store/users`, `store/rooms`, `store/messages` — orchestration across domains.
---
## Identity & credentials
```
Home profile (NgRx User)
id / username / displayName / homeSignalServerUrl
├─ AuthTokenStore (legacy per-URL token) metoyou.authTokens
├─ SignalServerCredentialStore metoyou.signalServerCredentials
│ { serverUrl → userId, token, provisioned? }
└─ ProvisionSecretStore (per home user id)
Electron safeStorage | web sessionStorage
```
**Actor resolution for a room:** `SignalServerAuthService.resolveActorUserIdForServer(sourceUrl, homeOderId)` — foreign URL must use provisioned `userId`.
**Identify on socket:** `SignalingTransportHandler.getIdentifyCredentialsForSignalUrl(url)` must resolve token+actor for that URL (store fallback).
**Danger:** `getIdentifyCredentials()` returns **home** credential — used for some localOderId / polite-peer paths → cross-signal initiator bugs (`06`, `09`).
---
## Room ↔ signal affinity
```
Room { id, channels[], sourceUrl, sourceId, ... }
RoomSignalingConnection
ensureCredentialForServerUrl(sourceUrl)
identify(actor for sourceUrl)
join_server / view_server on that WS
ServerSignalingCoordinator
maps serverId ↔ signalUrl ↔ peers
```
Fallback: try other online compatible endpoints on outage; **do not** treat Cloudflare 521/522 as “client incompatible”. Non-federated: peers must converge on same endpoint for that room.
---
## Realtime composition root
`WebRTCService` (`realtime-session.service.ts`) wires:
- `SignalingTransportHandler` → many `SignalingManager` (one WS per URL)
- `PeerConnectionManager` → negotiation, DC, recovery
- `MediaManager` / noise / screen share
- `WebRtcStateController` (signals)
Inbound WS → `SignalingMessageHandler` → users/rooms/voice/chat effects.
---
## Message send path (server channel)
```
Composer → MessagesActions.sendMessage({ id? })
→ local DB + NgRx
→ DC broadcast chat-message (+ message-revision)
→ WS chat_message fallback (room members)
→ account_sync to sibling devices
```
Receive gates: room must be current or saved; channel scoping for text.
---
## Voice join path
```
UI join channel
→ VoiceSessionFacade startSession
→ leave previous exclusive target
→ enableVoice / heartbeat
→ voice_state on WS (+ DC control)
→ MediaManager.syncVoiceRouting (same channel only)
→ VoicePlaybackService for remote same-channel peers
```
Peer PC may already exist from presence mesh; join mainly attaches tracks + announces state.
---
## Call / DM delivery path
```
DirectCallService / DirectMessageService
→ PeerDeliveryService
1) data channel if open
2) signaling forward (targetUserId)
3) offline queue (DM) / silent fail (call if ignored)
```
Inbound call admission: `direct-call-participant-identity.rules.ts` (aliases — implemented).
Outbound routable id pick: **documented in LESSONS as fixed; symbols not in tree** — see `10-code-lies`.
---
## Recovery constants (cheat sheet)
| Constant | Value |
|----------|-------|
| WS reconnect backoff | 1s → 30s |
| Connect timeout | 5s |
| Keepalive interval / ack timeout | 25s / 10s |
| Health probe | 5s |
| Peer disconnect grace | 10s |
| Peer reconnect | 5s × 12 then silent abandon |
| DC recovery grace (closing) | 2.5s |
| Non-initiator give-up | 5s |
| Offer-sent grace | 20s |
| Inventory / full sync limit | 20_000 |
| Sync batch | 200 |
| Sync poll | 10s fast / 15 min slow |
Source: `realtime.constants.ts`, `message-sync.rules.ts`.
---
## Server responsibilities (only when needed)
Default agent scope excludes `server/` unless packet expands. Know this:
- Serializes WS handlers per connection (identify-before-join).
- Relays RTC only when peers share membership (DM types exempt).
- No message persistence.
- `voiceActive` routes offers to owning connection.
Canonical envelopes: `agents-docs/features/signaling.md`.