π Landing page: https://sohaibirfann.github.io/dfs/
A private, invite-only desktop app for sharing files inside a small group β where every file is end-to-end encrypted on your device and stored across the group members' own machines instead of a company's cloud.
Mental model: "WhatsApp groups, but for file storage." One person in the group runs a small coordinator on the local network; everyone installs the app, joins with an invite, and files live distributed across just those members. No central service, no company holding your data.
- Features
- How it works
- Getting started (for a group)
- Hosting the coordinator
- Security model
- Tech stack
- Repo layout
- Development
- Build & release
- Limitations & scope
- License
Privacy & storage
- End-to-end encrypted β files are encrypted on your device (AES-256-GCM) before they ever leave it. The server only ever sees ciphertext.
- Distributed β each file is chunked and spread across the group's machines with a per-group replication preset (minimal / balanced / max), so files survive members going offline.
- Zero-knowledge coordinator β the server holds no files and no keys, only metadata (groups, presence, which chunk lives where).
Groups & invites
- Invite-only groups with owner/member roles, rename + emoji/color identity, transfer ownership, leave/remove members.
- Invites with expiry presets, one-time-use codes, revocation, and a per-invite "who redeemed it" list.
- Member presence (who's online) and per-member storage contribution.
Files
- Background uploads/downloads with live progress in a docked transfer panel (Transfers / Completed tabs, minimizes to a progress ring).
- Gallery + list views; image/video/PDF/text previews with a lightbox (β/β navigation); encrypted thumbnails generated at upload so previews are cheap.
- Multi-select β bulk download (zip or individual) and bulk delete; rename; overwrite confirmation; drag-and-drop upload.
Desktop
- Native Electron app β frameless window, live connection indicator, desktop notifications (file added / member joined), themes, configurable coordinator address, embedded storage node, and auto-update.
ββββββββββββββββββββββββββββββββ
β COORDINATOR (self-hosted) β β run by one group member
β control plane only β on the local network
β β’ groups / members / presenceβ
β β’ chunk β node map β
β β’ relays ciphertext β
β β’ NO files, NO keys β
βββββββββββββββββ¬ββββββββββββββββ
HTTP on the LAN
ββββββββββββββββββββββ¬ββββββ΄βββββββ¬βββββββββββββββββββββ
ββββββΌβββββ ββββββΌβββββ ββββββΌβββββ ββββββΌβββββ
β Member β β Member β β Member β β Member β
β app + β β app + β β app + β β app + β
β storage β β storage β β storage β β storage β
βββββββββββ βββββββββββ βββββββββββ βββββββββββ
Encrypted chunks are stored across members that contribute storage.
- Encrypt β a file is encrypted on your device with the group's key, then split into chunks. Plaintext never leaves the device.
- Distribute β encrypted chunks are spread across members' storage nodes (with replication), coordinated by the server, which only ever relays ciphertext.
- Access β any member fetches the chunks and decrypts on-device with the shared group key (delivered through the group's invite).
You need one machine to act as the host (runs the coordinator); everyone else just installs the app.
- Run the coordinator from this repo β Docker or Node
(see backend/DEPLOY.md). It prints its address, e.g.
http://192.168.1.50:5000. - Open the firewall β run
backend/open-firewall.ps1as Administrator (opens the coordinator + storage-node ports on your local network).
- Install the app β download
DFS Setup <version>.exefrom Releases and run it. (It's unsigned, so Windows SmartScreen shows a warning β More info β Run anyway.) - Connect β on first launch, enter the host's address
(
http://192.168.1.50:5000). The dot in the title bar turns green when connected. - Sign up. The host creates a group and shares an invite; others use Join with code.
- Contribute storage β in Settings β Storage, turn on Contribute on the machines that should hold the group's files (at least one).
- Share files β upload a file; it's encrypted on your device and distributed to the group. Anyone in the group can download and decrypt it.
The coordinator is a small Node/Express + SQLite + Socket.io server. One group member runs it; it holds no files or keys. Full instructions, including a Docker Compose setup, are in backend/DEPLOY.md. In short:
cd backend
cp .env.example .env # set JWT_SECRET (and NODE_SECRET for dev nodes)
docker compose up -d --build # or: node app.jsIt prints http://<your-lan-ip>:5000 on startup β that's the address members
enter. Members authenticate their storage node with their own login (JWT), so the
host doesn't need to hand out any shared secret.
- AES-256-GCM, performed client-side before anything is uploaded.
- Keys live only on devices β a group's key is generated on-device and shared through the group's invite link; it is never sent to or stored on the server.
- Zero-knowledge coordinator β it stores metadata only (groups, presence, chunkβnode map) and only ever relays ciphertext.
- Group isolation β each group has its own key; groups can't see or decrypt each other's data.
- Integrity β each chunk is SHA-256 hashed; corrupted/tampered chunks are rejected on download.
Honest caveats (see Limitations): the group key is a bearer secret carried in the invite, it lives only on the device (no built-in backup/recovery beyond re-inviting), and the installer is unsigned.
| Layer | Tech |
|---|---|
| Desktop shell | Electron (frameless window, embedded storage node, auto-update) |
| UI | React + Vite + Tailwind CSS v4 |
| Coordinator | Node.js + Express + Socket.io |
| Storage | SQLite (better-sqlite3) for metadata; encrypted chunks on members' disks |
| Crypto | Web Crypto API (AES-256-GCM), client-side |
| Path | What it is |
|---|---|
backend/ |
The coordinator (Express + SQLite + Socket.io) and a standalone storage-node server (nodeServer.js) for dev/headless use. Includes DEPLOY.md and open-firewall.ps1. |
frontend/ |
The React + Vite app (UI), bundled into the desktop client. |
desktop/ |
The Electron shell β window, settings, embedded storage node, packaging + auto-update (RELEASING.md). |
Prerequisites: Node.js 18+.
npm install
npm --prefix backend install && npm --prefix frontend install && npm --prefix desktop install
# coordinator secrets
cp backend/.env.example backend/.env # fill JWT_SECRET, NODE_SECRET
# generate each: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
npm run dev # coordinator + storage node + web UI + desktop app
npm run dev:web # β¦without the Electron windowDefault ports: coordinator 5000, web UI 5173, dev storage node 7001.
| Command | Where | Does |
|---|---|---|
npm run dev |
root | coordinator + storage node + web + desktop |
npm run dev:web |
root | same, without the Electron window |
npm run tunnel |
root | coordinator + a Cloudflare quick tunnel (expose it temporarily) |
npm start |
backend/ |
run the coordinator (node app.js) |
npm run node1 |
backend/ |
run a standalone storage node on :7001 |
npm test |
backend/ |
backend group tests |
npm run dev / build |
frontend/ |
Vite dev server / production build |
npm run pack / dist |
desktop/ |
unpacked build / installer |
- Package the app:
cd desktop && npm install && npm run distβdesktop/release/DFS Setup <version>.exe. On Windows, enable Developer Mode so the build's signing-tool extraction succeeds. - Ship updates: the app auto-updates from GitHub Releases. Bump the version,
build with
--publish, publish the draft release β details in desktop/RELEASING.md.
- LAN / self-hosted only. The coordinator must be reachable by all members β i.e. one local network (or a coordinator the group exposes themselves, e.g. via a tunnel/VPS). Cross-internet peer-to-peer (WebRTC) was explored but is out of scope.
- Bytes relay through the coordinator (still ciphertext) rather than flowing directly peer-to-peer.
- Group key is device-local β clearing app storage or moving devices means re-joining via an invite to get the key again; there's no key backup/recovery.
- Invite is a bearer secret β anyone with an unexpired invite can join and decrypt; removing a member doesn't rotate the key. Use short-lived / one-time invites.
- Unsigned installer β Windows SmartScreen warns on first install.
- Whole-file in-memory crypto β very large files are encrypted/decrypted in memory.
MIT Β© Sohaib Irfan