PR #4 of the multi-device roadmap — desktop client groundwork. The shell compiles and runs end-to-end on top of a v2.2.0 node; sections are placeholders that later alphas fill in with real chat / feed / wallet / contacts / settings content shared with the mobile client-app. Scaffold: * Vite + React + TypeScript renderer; Electron main/preload TS compiled via a separate tsconfig. * npm scripts — `dev` (concurrent Vite + Electron), `build` (installer via electron-builder), `typecheck`. * electron-builder targets: .dmg / .exe / .AppImage + .deb. * CSP pins script-src 'self'; connect-src left open so the renderer can hit any configured node. Electron main + preload: * Frame-less window, hiddenInset on macOS, custom-overlay on Windows, drag region via CSS -webkit-app-region: drag on our TitleBar. * contextIsolation on, nodeIntegration off, sandbox off (needed for safeStorage in preload). * window.dchain.keyfile.{load,save,delete,encryptionAvailable} — keyfile lives in the OS keychain via Electron safeStorage, with a plaintext fallback for OSes without an encryption backend. * window.dchain.dialog.{openFile,saveFile}, .fs.{readText,writeText}, .app.{version,platform}. Everything else still goes over plain fetch() in the renderer. Shell (src/shell/): * TitleBar — draggable 32px strip; DChain brand. * NavBar — left 72px rail, six sections + Cmd+1..5 keybinds. * StatusBar — ● online/connecting/offline dot, node URL, current chain height (polls /api/netstats every 5s). * Shell — composes the 3 panes; picks { List, Detail } by active section. Sections (all stubs — filling in alpha5+): * Messages, Feed, Contacts, Profile — SectionPlaceholder with notes. * Wallet — shows the balance reading from /api/address/{pub} as a first real data binding. * Settings — node-URL card with live ping + commit, identity card (shows pub key), about card (reads Electron app.version via IPC). Auth (src/auth/Welcome.tsx): * Create — generates Ed25519 + X25519 via tweetnacl, saves via IPC. * Import — Electron dialog.openFile → parses node.json → saves. * Pair — stub routed; real poll loop reuses the mobile flow in alpha5. Lib (src/lib/): * types.ts — KeyFile / Contact / Message / NodeSettings mirroring client-app wire formats. * storage.ts — keyfile via IPC, settings + contacts + device-registered marker via localStorage. * api.ts — fetch wrapper with setNodeUrl + onNodeUrlChange; getNetStats, getIdentity, fetchDevices, getBalance bindings. * store.ts — zustand { booted, keyFile, settings, contacts, section }. docs/ROADMAP.md — desktop subsection updated with per-alpha breakdown. Next (alpha5): Messages section wired to the relay mailbox, full conversation view, and the pairing poll loop.
3.3 KiB
DChain Desktop
Electron shell for the DChain messenger and social feed.
Same functionality as the mobile client-app, re-imagined with a keyboard-first, 3-panel desktop layout:
┌──────────────────────────────────────────────────────────┐
│ DChain │ titlebar (drag)
├──────┬───────────────────┬────────────────────────────────┤
│ nav │ list │ detail │
│ 72px │ 340px fixed │ flex 1 │
├──────┴───────────────────┴────────────────────────────────┤
│ ● online · node.example:8080 · height 10942 │ status bar
└──────────────────────────────────────────────────────────┘
Sections (left rail): Messages · Feed · Wallet · Contacts · Settings · Profile.
Quick start
cd desktop
npm install
npm run dev # concurrently: Vite dev server + Electron
The first boot will show the Welcome screen. Pick Create to generate
fresh keys, or Import a node.json exported from the mobile client.
Build
npm run build # produces dist/ (renderer) + dist-electron/ (main) + installers
Default installers are built with electron-builder: .dmg on macOS,
NSIS .exe on Windows, AppImage + .deb on Linux. Adjust build.* in
package.json for signing / notarisation.
Layout
electron/— main + preload. TypeScript, compiled todist-electron/bytsc -p electron/tsconfig.json.src/— renderer. React + Vite.@/aliases tosrc/.src/shell/— 3-panel chrome.src/sections/— one folder per nav section, each exports{ List, Detail }.src/auth/Welcome.tsx— shown when no key is loaded.src/lib/— api, storage, store, types. Mirrors (without React-Native deps) the relevant pieces of../client-app/lib/.
Security model
Master Ed25519 priv lives in the OS keychain via Electron safeStorage
(macOS Keychain / Windows DPAPI / libsecret). A renderer compromise
cannot read or exfiltrate the key — it always travels through
window.dchain.keyfile.* IPC, which main.ts validates and mediates.
contextIsolation: true, nodeIntegration: false. CSP in index.html
pins script sources to 'self' while allowing connect-src * so the
renderer can hit any node the user configures.
Pairing (v2.2.0-alpha5+)
Desktop will reuse the same 6-digit-code + relay-envelope handshake as
the mobile client. The scaffold in src/auth/Welcome.tsx stubs the
button until the polling loop lands.
Multi-device fan-out
When the node is at v2.2.0-alpha1+, lib/api.ts:fetchDevices returns
every linked X25519 pub for a given identity; the sender then encrypts
one envelope per device. Legacy nodes return an empty array and the
client falls back to IdentityInfo.x25519_pub, preserving the
pre-multi-device behaviour.