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.
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
# 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`.
|
||||
Reference in New Issue
Block a user