2026-08-07 21:20:48 +02:00
|
|
|
# OwnCord Client (Tauri v2)
|
|
|
|
|
|
|
|
|
|
TypeScript frontend (Vite, vanilla TS — no React/Vue) plus a deliberately thin
|
|
|
|
|
Rust backend in `src-tauri/` for native APIs only. LiveKit handles voice/video.
|
|
|
|
|
|
|
|
|
|
## Layout
|
|
|
|
|
|
|
|
|
|
- `src/stores/` observable stores · `src/lib/` protocol, WS, voice, E2EE ·
|
|
|
|
|
`src/pages/`, `src/components/` UI
|
|
|
|
|
- `src/lib/protocolTypes.ts` and `src/generated/` are generated — see the root
|
|
|
|
|
CLAUDE.md
|
2026-08-28 06:54:32 +02:00
|
|
|
- `tests/unit`, `tests/integration`, `tests/contract` (vitest, jsdom) ·
|
|
|
|
|
`tests/e2e`, `tests/e2e/admin`, `tests/e2e/native` (Playwright) ·
|
2026-08-07 21:20:48 +02:00
|
|
|
`tests/browser` (vitest browser mode)
|
2026-08-28 06:54:32 +02:00
|
|
|
- A test whose assertions read, import or execute a **`Server/`-owned**
|
|
|
|
|
artifact belongs in `tests/contract`, not `tests/unit` — `src-tauri/` is
|
|
|
|
|
part of this component, so reading it is an ordinary unit test. The rule
|
|
|
|
|
is in [docs/contributing.md](../docs/contributing.md#testing)
|
|
|
|
|
- `src/platform/` does **not** exist yet. Where the desktop/browser seam will
|
|
|
|
|
go, and which 20 files hold the native imports that must move behind it, is
|
|
|
|
|
recorded in
|
|
|
|
|
[docs/architecture/platform-contracts.md](../docs/architecture/platform-contracts.md).
|
|
|
|
|
Building it is B7 — do not start it as a side effect of another change.
|
2026-08-07 21:20:48 +02:00
|
|
|
|
|
|
|
|
## Gotchas
|
|
|
|
|
|
2026-08-22 06:48:58 +02:00
|
|
|
- Node's native Web Storage (Node 22+) shadows jsdom's `localStorage`;
|
|
|
|
|
`tests/setup.ts` replaces it with an in-memory shim, so the suite runs on
|
|
|
|
|
modern Node without `--no-experimental-webstorage`. If storage tests fail
|
|
|
|
|
en masse, suspect that shim before your change. CI pins Node 24.
|
2026-08-09 11:50:12 +02:00
|
|
|
- `src/lib/dispatcher.ts` is the single WS-event entry point **into the
|
|
|
|
|
stores**: server events reach domain stores only through a `ws.on(...)`
|
|
|
|
|
subscription registered there. Other modules do register their own
|
|
|
|
|
`ws.on(...)` handlers for page-local UI (`main.ts`, `MainPage.ts`,
|
|
|
|
|
`ChannelController.ts` — ringing, overlays, slow-mode timers); that is fine
|
2026-08-28 06:54:32 +02:00
|
|
|
as long as they only _read_ store state. Writing a store from one of those
|
2026-08-09 11:50:12 +02:00
|
|
|
handlers is the violation, and `local/no-store-write-in-ws-on` now fails the
|
|
|
|
|
build on it.
|
2026-08-07 21:20:48 +02:00
|
|
|
- Voice sessions are superseded, not cancelled. `LiveKitSession` re-entry
|
|
|
|
|
points check whether a newer attempt owns the shared state before tearing
|
|
|
|
|
anything down, so cleanup in an aborted path must be scoped to that attempt's
|
|
|
|
|
own room — a global `leaveVoice()` there kills the live session.
|
|
|
|
|
- Voice E2EE is key-holder based with TOFU identity pinning. Anything touching
|
|
|
|
|
`livekitE2EE.ts` or `identity.ts` must preserve the epoch/keypair staleness
|
|
|
|
|
guards and must never report an unverified peer as verified.
|
|
|
|
|
- Do not run `npm run tauri build` locally; the desktop build is CI-only.
|
|
|
|
|
- Formatting is prettier-enforced; match the surrounding code rather than
|
|
|
|
|
reasoning about style.
|