MessageList polled every three seconds and ChatPage bumped a refreshKey after
each send. Both are gone; messages now arrive over a socket.
The connection lives in the Tauri Rust layer rather than in the WebView. Two
reasons: the handshake can carry a real Authorization header, which is the only
channel that nginx, Apache and Caddy do not write to their access logs by
default, and tungstenite surfaces the full HTTP response on a rejected
handshake. The browser API is required by spec to collapse every failure into
close code 1006, so "token expired" and "server down" would be
indistinguishable — which matters once the client holds connections to several
servers and has to show a meaningful state for each.
The socket only carries {conversation, seq}. Rust does the backfill over REST
and hands resolved entities to the frontend, so the UI keeps no cursor logic:
it replaces by id and sorts by seq.
The client now supplies the message id. Without it an optimistically rendered
message cannot be matched against the copy that arrives over the realtime
channel and shows up twice; it also makes a retry after an unclear network
error a no-op instead of a duplicate.
icons/icon.ico is generated from the existing icon.png — tauri-build requires
it on Windows, and without it the crate does not build there at all.
Not yet done, marked as TODO in the code: token refresh (the connection ends
at expiry with 4001), the server list replacing BASE_URL, optimistic rendering,
and the DM-versus-channel rule for background fetching.
rustification-client
Desktop chat client for Rustification. Built with Tauri v2 (Rust backend) and React + TypeScript (frontend).
Tech stack
| Layer | Technology |
|---|---|
| Desktop shell | Tauri v2 |
| Frontend | React 18 + TypeScript, Vite |
| HTTP | reqwest (Rust) via Tauri commands |
| Persistence | tauri-plugin-store (JWT token, user ID) |
| Build (Linux) | nix-shell |
Features
- Login / Register — authenticates against the rustification-server, stores JWT via tauri-plugin-store
- Workspace list — browse workspaces the current user belongs to
- Chat page — conversation list, message history, and real-time polling for new messages
- Message input — send messages to any conversation
- Member panel — view conversation members
- Settings page — user preferences
Prerequisites
- Rust + Cargo
- Node.js 18+ and npm
- Tauri v2 system dependencies (see below)
- A running rustification-server
System dependencies (Linux)
The project ships a shell.nix. If you use Nix, enter the dev shell first:
nix-shell
This provides: cargo, rustc, pkg-config, glib, gtk3, webkitgtk_4_1, libsoup_3, openssl, librsvg.
Without Nix, install the equivalent packages via your system package manager. On Debian/Ubuntu:
sudo apt install libgtk-3-dev libwebkit2gtk-4.1-dev libsoup-3.0-dev \
libayatana-appindicator3-dev librsvg2-dev
Getting started
# Install frontend dependencies
npm install
# Start in development mode (hot-reload frontend + Tauri window)
# Must be run inside nix-shell on NixOS / Nix systems
npm run tauri dev
The client defaults to http://localhost:3000/api/v1 as the server base URL (configurable at runtime via Settings).
Build
# Must be run inside nix-shell on NixOS / Nix systems
npm run tauri build
Output bundles (.deb, .AppImage, etc.) are written to src-tauri/target/release/bundle/.
Project structure
src/ # React frontend
├── main.tsx # Entry point
├── App.tsx # Router + AuthContext provider
├── theme.ts # MUI / global theme
├── styles.css
├── contexts/
│ └── AuthContext.tsx # Auth state (token, user ID, login/logout)
├── pages/
│ ├── LoginPage.tsx
│ ├── WorkspacePage.tsx
│ ├── ChatPage.tsx
│ └── SettingsPage.tsx
└── components/
├── ConversationList.tsx
├── MessageList.tsx
├── MessageInput.tsx
├── MemberPanel.tsx
└── Toast.tsx
src-tauri/ # Tauri Rust backend
├── Cargo.toml
└── src/
├── lib.rs # Tauri builder, plugin + command registration
├── main.rs
├── state.rs # AppState: reqwest client, token, base URL
├── models.rs # Shared response types
└── commands/
├── user.rs # login, register, refresh_session, logout
├── workspace.rs # list_workspaces
├── conversation.rs # list_conversations
└── message.rs # list_messages, send_message
Tauri commands
The frontend calls these via invoke():
| Command | Description |
|---|---|
login |
Authenticates and stores JWT + user ID |
register |
Registers a new account |
refresh_session |
Refreshes the JWT using the stored refresh token |
logout |
Clears stored credentials |
list_workspaces |
Returns workspaces for the current user |
list_conversations |
Returns conversations for a workspace |
list_messages |
Returns messages for a conversation |
send_message |
Posts a new message |
Server connection
The default base URL is http://localhost:3000/api/v1. To point the client at a different server, update the URL in Settings — it is persisted in the Tauri store across sessions.