01.First launch & orientation
Desktop vs web binary, embedded Postgres bootstrap, auto-login local user, the topbar, the Cmd-K tile launcher, and the boot path from main.rs to app.js.
One workspace, two binaries
TraderView ships from a single Rust workspace and produces two executable shells. Both depend on the same library crates (traderview-core, traderview-db, traderview-import, traderview-expense, traderview-ocr) and serve the same vanilla-JS frontend verbatim. The only thing that distinguishes them is the Postgres backend and the auth layer.
| Mode | Binary | Postgres | Auth | Audience |
|---|---|---|---|---|
| Desktop | traderview-desktop | embedded (downloaded via postgresql_embedded on first launch) | auto-login local user | single user, offline |
| Web | server | external (DATABASE_URL) | argon2 + JWT bearer | multi-user, hosted |
Pick the mode that matches your deployment and skip the rest of the table — the UI is identical from here on.
Desktop boot — embedded Postgres bootstrap
On macOS the app installs into /Applications and launches like any Tauri v2 app. The Rust side does the following before the WebView is shown:
- Locate the app-data dir.
~/Library/Application Support/com.menketechnologies.traderview/pg/(macOS),~/.local/share/com.menketechnologies.traderview/pg/(Linux),%APPDATA%\com.menketechnologies.traderview\pg\(Windows). - Recover from a stale lock. If
postmaster.pidexists, the bootstrap reads the recorded PID and checks if that process is still alive. If not, the lockfile is removed — a SIGKILL'd parent on the previous run no longer blocks startup. - Reuse the persisted password. A random password file (mode 0600) is written on first init and reused on every subsequent launch so authentication survives across runs.
- Download Postgres if missing. First launch fetches a portable PostgreSQL into
~/.theseus(~80 MB). Subsequent launches reuse that cache. - Start the cluster, bind to a random localhost port, run migrations. 46 migrations under
migrations/0001_*.sqlthroughmigrations/0046_*.sqlare applied in order; new migrations apply on the next launch. - Hold the
Embeddedhandle acrossaxum::serve. The worker thread owns the handle for the lifetime of the server and drops it explicitly after serve returns. An earlier version dropped at function exit, which silently calledpg_ctl stopmid-request — every subsequent query timed out with "pool timed out". - Start the axum router on the localhost port and the Tauri WebView. The WebView loads
frontend/index.htmlvia thetauri://localhostcustom scheme.
No external services. No cloud account. No network round-trip required after the first-run download. Embedded Postgres is shut down cleanly when you quit the app.
Web boot — external Postgres + JWT
The web binary (cargo run --bin server or the server binary from a release archive) expects two required environment variables and a handful of optional ones:
| Variable | Default | Purpose |
|---|---|---|
DATABASE_URL | required | Postgres connection string. Pool is max_connections=32, min_connections=4, acquire_timeout=15s. |
TRADERVIEW_JWT_SECRET | required | HMAC secret for JWT signing. Rotate to invalidate outstanding tokens. |
TRADERVIEW_BIND | 0.0.0.0:8080 | axum listen address. |
TRADERVIEW_CORS_ORIGIN | * | CORS allowlist. |
TRADERVIEW_LOG | info | tracing-subscriber env-filter directive (e.g. traderview_web=debug,tower_http=info). |
First load: visit / → register a user → log in → JWT lands in localStorage → every fetch from js/api.js attaches Authorization: Bearer …. The /health, /config, /auth/*, and /client-errors routes are public; everything else requires the bearer header.
The topbar — what every chip means
Across the top of every view sits a thin strip with eleven actions and a search box. Left to right:
- Logo + brand — wordmark and version string (synced at build time from
Cargo.toml). - Account picker — switch between accounts. Each broker import lands in its own
accountsrow; the picker scopes every downstream query. - Quick-nav chips — Dashboard · Trades · Journal · Reports · Watchlists · Charts · Live · Scanner. These are the same 8 routes the global keyboard shortcuts target (
Cmd+Opt+D/T/J/R/W/C/L/M). - Symbol search — full-text autocomplete over
symbols+ your watchlists. Enter jumps to the per-symbol research dossier (step 08). - Cmd-K launcher button — opens the 648-tile categorized launcher. The same overlay opens on
Cmd+Kfrom any view. - Help / tutorial — the
?key (no modifier) opens an in-app keyboard cheat-sheet across three searchable tables (48 globals + 70+ scoped ctxmenu items + view-scoped binds).
The Cmd-K launcher — 648 tiles, 10 categories
There is no fixed nav strip. The topbar carries the most-used routes; everything else lives in the Cmd-K launcher. Tiles are grouped into the categories below; type any substring to filter; Enter jumps to the first match.
⚡ Live Markets 6 tiles Scanner / Halts / Catalysts / Pre-market / Tape / Heatmap 📊 Trading 16 tiles Webull / Live Positions / Paper / New Trade / Plans / Hotkeys 📓 Journal 9 tiles Journal / AI Journal / Trade Reviews / Compare / Replay / Discipline 🔎 Charts & Research 178 tiles Charts / Research / Watchlists / Screener / Scanners / Sectors / … 📈 Reports 23 tiles Dashboard / Reports / R-Multiple / Forecast / Fill Quality / Tax / … 🧷 Strategy 7 tiles Backtest / Walk-forward / Custom Indicators / Strategy Alerts / Alerts / Webhooks 💬 Community 4 tiles Shares / Forum / Mentorship / Boards ⚙️ Admin 18 tiles Import / CSV Wizard / Exports / Accounts / Tags / Search / Settings / API tokens
The exact tile count drifts as views are added — the launcher reads its catalog from a single registry at boot, so the count in the README is generated, not typed. Pin / unpin tiles from the launcher to put your daily routes on the topbar.
Recommended first-run path
- Open the launcher (
Cmd+K), type "accounts", Enter — create an account row for each broker you'll import (step 02). - Import a broker CSV from the Import tile, or run the Generic column-mapping wizard for anything not on the built-in list.
- Run the FIFO roll-up —
POST /api/trades/rollupis called automatically on every import, but you can re-run it on demand from the Trades view. - Open the Dashboard (
Cmd+Opt+D) — equity curve + summary stats + world-markets snapshot grid all populate from the roll-up. - Press
?at any time for the in-app shortcut cheat-sheet.
Boot-time hardening you don't have to configure
- Per-view race tokens. Every navigation increments a global token in
app.js. Each view captures the token at render start and bails after eachawaitif it has gone stale — prevents thedocument.getElementById(...)-returns-null crash that hits naïve SPAs when slow async resolves into a replaced DOM. - Browser-side error sink.
window.onerror,unhandledrejection, and an overriddenconsole.errorall POST to/api/client-errors, queue-capped at 200 so a console-error loop can't run away. - WebKit SecurityError guards. Under Tauri's
tauri://localhostcustom scheme,navigator.sendBeacon,Notification.requestPermission,new Notification,speechSynthesis.speak, andlocalStorageall throw cross-origin SecurityErrors. Each is try/caught so boot survives. - CSP allows
ws://127.0.0.1:*so WebSocket connections to the dynamic backend port aren't blocked under the custom scheme. - Real concurrency for fan-out routes.
premarket::snapshot,markets::snapshot,market_data::quotes, andcompare::compareall usefutures_util::future::join_all; a previous serial-loop version blocked/api/premarket/snapshotfor 150 seconds and starved the pool.
~/Library/Logs/com.menketechnologies.traderview/app.log (macOS) or run the web binary in the foreground — both surface the actual sqlx error.