PolyHub is a P2P file sharing Electron app using Tailscale's mesh VPN for secure direct connections. Features brutalist UI with dark/light themes.
Main Process (Node.js) Renderer Process (React)
├── IPC Handlers ├── UI Components
├── Peer Server (TCP) ├── Gallery, Settings, Discovery
├── File Transfer └── Onboarding
└── Storage (electron-store)
↓
Tailscale VPN (100.x.x.x)
↓
Peer-to-Peer Network
- Electron v28.1.0 — Desktop framework
- React v18.2.0 — UI
- Vite v5.0.10 — Build tool
- Tailscale — VPN mesh network
- electron-store — Persistent storage
- 47777 — Control messages (JSON over TCP): PAIR_REQUEST, FILE_DELETE, PROFILE_UPDATE
- 47778 — File transfer (binary): 4-byte header length + JSON metadata + raw file stream
- User extracts Tailscale IP via
tailscale status - Generate pairing link:
polyhub://pair/{base64({name, ip, timestamp})} - Share link out-of-band
- Peer parses link and connects via TCP
Sender: Select files → Copy to sync folder → Generate metadata → Stream to peers (port 47778) Receiver: Listen on 47778 → Read header → Accumulate chunks → Write to sync folder → Update gallery
- Default sync folder:
~/Documents/PolyHub - Custom protocol:
polyhub-file://for thumbnail access - Metadata stored in electron-store JSON
{
id: "timestamp-random",
name: "file.jpg",
path: "C:\\Users\\...\\PolyHub\\file.jpg",
size: 2048576,
type: "jpg",
sharedBy: "Alice",
sharedAt: 1704723891234,
from: { name: "Alice", ip: "100.x.x.x" }
}{
profile: { name: "Alice", ip: "100.x.x.x" },
peers: [{ name: "Bob", ip: "100.x.x.x", addedAt: timestamp }],
sharedFiles: [{ id, name, path, size, type, sharedBy, sharedAt }],
settings: {
syncFolder: "path",
maxFileSize: 5, // GB
maxStorageSize: 5, // GB
notifications: true,
theme: "dark",
overlayShortcut: "Alt+D"
}
}- Function over form, sharp edges, hard shadows
- Monospace typography (JetBrains Mono)
- High contrast, minimal decoration
Dark: BG #0d0d0d, Surface #1a1a1a, Accent #ff6700 (Safety Orange) Light: BG #ffffff, Surface #f7f6f3, Accent #d44800
/— Onboarding (if no profile)/discovery— Peer pairing/gallery— File sharing/settings— Configuration
- TitleBar — Custom window controls
- Sidebar — Navigation
- Gallery — Drag-and-drop file sharing
- Settings — Profile, storage, theme, peers
- Overlay — Global drop zone (Alt+D)
- All traffic encrypted via Tailscale/WireGuard
- No authentication beyond Tailscale network
- No file integrity checking
- Context isolation enabled
- Node integration disabled in renderer
- CSP:
default-src 'self'; img-src 'self' data: polyhub-file:
src/
├── main/ # Electron main process
│ ├── main.js # Entry, IPC handlers
│ ├── peerServer.js # TCP P2P server
│ ├── store.js # Persistent storage
│ ├── tailscale.js # Tailscale integration
│ ├── preload.js # IPC bridge
│ ├── autoUpdater.js # Auto-update from GitHub
│ ├── overlay.html # Global drop overlay
│ └── notification-overlay.html # File notifications
└── renderer/ # React UI
├── App.jsx
├── components/ # TitleBar, Sidebar
├── pages/ # Gallery, Settings, Discovery
└── styles/
npm run dev— Start dev server + Electronnpm run build— Build for productionnpx electron-builder --win --x64— Build Windows installer
1. Update version numbers:
# Update version.json
{
"version": "4.1.2",
"buildDate": "2026-02-XX",
"name": "Poly-Hub"
}
# Update package.json
"version": "4.1.2"2. Commit and tag:
git add .
git commit -m "Version 4.1.2 - Description of changes"
git tag v4.1.2
git push origin main
git push origin v4.1.23. GitHub Actions automatically:
- Builds Windows installer (NSIS):
Poly-Hub-Setup-4.1.2.exe - Builds portable EXE:
Poly-Hub-Portable-4.1.2.exe - Creates GitHub release
- Uploads both installers + update metadata (
latest.yml,app-update.yml) to release
4. Auto-update triggers:
- Users with older versions get update prompt on next launch
- Downloads and installs automatically
- App restarts with new version
- All settings/files preserved
CRITICAL: Auto-Updater Configuration To ensure auto-updates work properly, the following must be maintained:
-
Never set
forceDevUpdateConfig = truein production (src/main/autoUpdater.js)- This forces electron-updater to look for
dev-app-update.ymlinstead of production files - Production builds must use standard
latest.ymlandapp-update.yml
- This forces electron-updater to look for
-
GitHub Actions must upload both update metadata files (
.github/workflows/release.yml):files: | dist-electron/Poly-Hub-Setup-*.exe dist-electron/Poly-Hub-Portable-*.exe dist-electron/latest.yml dist-electron/app-update.yml dist-electron/*.blockmap
-
NSIS config must enable differential updates (
package.json):"nsis": { "differentialPackage": true, ... }
-
Verify update files are generated after building:
- Check
dist-electron/containslatest.ymlandapp-update.yml - Both files should reference the correct version and download URLs
- Check
Note: Auto-updater checks GitHub releases every 6 hours and on startup. Manual check available in Settings.
- NSIS installer automatically replaces old versions
- Settings stored in
%APPDATA%\poly-hub\(preserved on update) - Sync folder in
Documents\PolyHub\(preserved on update) - Auto-start on Windows boot (hidden/minimized)
- Desktop and Start Menu shortcuts created
- Define in
main.js:ipcMain.handle('action', async (e, arg) => {}) - Expose in
preload.js:action: (arg) => ipcRenderer.invoke('action', arg) - Use in renderer:
await window.electronAPI.action(arg)
- P2P file sharing over Tailscale
- Real-time sync to all peers
- Drag-and-drop interface
- Storage limits (5GB default)
- Global drop overlay (Alt+D, ESC to close)
- Smooth liquid animations on overlay
- Auto-hide overlay after file drop (1s delay)
- File notification overlays with accept/decline
- Auto-start on Windows boot (minimized)
- Auto-update from GitHub releases
- Dark/light themes
- File thumbnails
- Peer status indicators
- Auto-update installer (preserves settings)
- File notifications with accept/decline
- Folder sync
- Resume interrupted transfers
- End-to-end encryption
- Hash check sent/recivied to double confirm it has not been tainted
- Auto-update fails with "ENOENT: no such file" error: Fixed in v4.1.2 - removed
forceDevUpdateConfigflag - Blank screen on startup (v4.0.1-4.0.3): Fixed in v4.0.4 - incorrect production build path
- Files not transferring: Check Tailscale connection, firewall (ports 47777/47778)
- Thumbnails missing: Verify file path, check CSP allows
polyhub-file: - Peers not connecting: Verify Tailscale running, correct IP (100.x.x.x)
- Overlay not appearing: Check global shortcut (Alt+D), restart app
- Auto-start not working: Reinstall with NSIS installer
Version: 4.1.2 | Last Updated: February 2026