Implements the next four gaps from the client UX spec (docs/architecture/ux).
Connection status as single source of truth (spec §3):
- main.ts registers the one writer: ws.onStateChange → toConnectionStatus
(new 5→3 state mapper exported from ws.ts) → ui.store.connectionStatus.
- Consumers now subscribe to the store instead of ad-hoc ws wirings: the
MainPage reconnect banner (which also gains a "Disconnected" state via
ServerBanner.showDisconnected instead of going stale), ChannelController
composer gating + per-click send guard, and the UserBar presence picker.
- Fixes a latent production bug: SidebarArea never passed ws to UserBar, so
the status picker was permanently disabled and its presence_update path
dead. It now gates on the store and receives the ws send path.
- The one-shot connected-overlay wiring stays on ws.onStateChange by design
(it needs the exact internal transition); LiveKit voice reconnection stays
independent ("retrying underneath").
Transport backpressure surfaced (spec §5):
- ws.ts sendRaw no longer drops local send failures silently: send() passes
the envelope id, and failures notify a new onSendFailure(id, code)
listener — channel full → NETWORK, closed/not-open → OFFLINE (deferred a
microtask on the not-open path so the optimistic row registers first).
- The dispatcher fails the matching pending row via markSendFailed, exactly
like a server error reply; id-less sends (heartbeat) and fire-and-forget
sends (typing, presence) stay silent by design. MessageList renders the
new NETWORK reason ("Connection problem — message not sent").
uploadFile honors global 401 handling (spec §5):
- api.uploadFile now calls onUnauthorized and throws ApiClientError(401)
like every other REST call; main.ts sets the "Your session expired — sign
in again." transient error so the connect page shows the reason.
History fetch loading/error states (messaging.md §1):
- messages.store gains per-channel historyLoadState (loading/error, absent
= idle) with setChannelLoading/setChannelLoadError; setMessages and
clearChannelMessages clear it.
- MessageController.loadMessages sets loading synchronously before the
fetch and marks error inline instead of a toast; MessageList renders an
in-region spinner placeholder or an inline error + Retry (onRetryLoad
re-invokes loadMessages via ChannelController).
Also fixes two pre-existing eslint errors in api.ts (redundant assertions).
Docs: the corresponding gap callouts in docs/architecture/ux are updated
(README §3/§5, messaging.md §1/§3/§6, channels-members-dms.md block-gating
note no longer claims the composer lacks a read-only mode).
Verified: tsc + full client unit suite (3225 tests) + oxlint/eslint +
prettier all green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
OwnCord Architecture Blueprints
Verified against: commit ddc49f0, 2026-07-19
Companion audit: docs/audit-2026-07-19.md
This directory is the curated architectural map of OwnCord — the "blueprints" for the whole system. Every diagram is a Mermaid fenced block (GitHub renders these natively) followed by a prose explanation and a Source of truth file list.
Index
| Doc | Diagrams | Covers |
|---|---|---|
| system-overview.md | D1 System context, D8 Deployment topology | All processes, trust boundaries, ports, single-instance constraints |
| server.md | D2 Server package map, D3 REST request lifecycle | Go package structure, DB-access styles, middleware chain |
| websocket.md | D4 WS connect / replay / dispatch | Real-time engine: auth handshake, 3-tier reconnect replay, backpressure, V1/V2 dispatch |
| data-model.md | D5 Entity-relationship overview | All 23 tables from migrations 001–015, grouped by domain |
| voice-e2ee.md | D6 Voice + E2EE flow | LiveKit token flow, loopback TLS tunnel, ECDH key-holder relay |
| client.md | D7 Client module map | Tauri client: bootstrap, dispatcher, stores, Rust sidecars (structure, as-built) |
| ux/ | UX flow + state diagrams | Client behavior spec (target state): what every view does and how it reacts to events, permissions, and failure |
Structure vs. behavior
client.md maps the client as-built (modules, stores, wiring). The ux/ set is the complementary behavior spec — prescriptive (to-be) flows for every view, with per-view state matrices and event→reaction maps. Where today's code diverges from the target, the UX docs carry dated ⚠ Current gap callouts, so the set doubles as a UX improvement backlog.
Maintenance rule
These documents are curated, not generated. The rule that keeps them honest:
If a PR changes the structure of anything listed in a diagram's Source of truth list (new package, new table, new message type, changed flow), that PR updates the corresponding diagram in the same change.
Diagrams reference stable identifiers (package names, table names, message-type strings) rather than line numbers wherever possible. Line-number evidence lives in the dated audit reports, which are point-in-time snapshots by design.
Relationship to other docs
docs/api.md,docs/protocol.md,docs/schema.mdare the reference specs (request/response shapes, wire formats, DDL). These blueprints describe structure and flow, not payload shapes. Known drift between the specs and the code is catalogued in audit-2026-07-19.md §2.docs/client-architecture.mdpredates the abandonment of the Solid.js migration; client.md reflects the current state.