On Windows, `wait-on tcp:5173` can hang forever because Vite's default
host ('localhost') binds to IPv6 (::1) while wait-on probes 127.0.0.1.
concurrently then never triggers electron:dev, leaving Vite running in
the foreground with no Electron window.
Pin both sides to IPv4:
* `vite --host 127.0.0.1` — force the dev server off ::1.
* `wait-on http://127.0.0.1:5173` — real HTTP GET instead of raw TCP,
robust against the same dual-stack oddity.
* VITE_DEV_SERVER_URL switched to the matching 127.0.0.1 so Electron
loads the same origin the CSP checks against.
Symptom before: `npm run dev` printed Vite banner then sat there silent.
Symptom after: electron:dev line appears within a second, Electron
window opens with the Welcome screen.
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.