The Problem

Transferring files between devices on a local network without a central server is technically demanding: it requires WebRTC signaling, ICE candidate negotiation, data-channel setup, and NAT traversal. Most consumers lack the infrastructure to wire these pieces together, and existing solutions either rely on cloud relays (incurring bandwidth costs) or demand manual configuration of ports, certificates, and tunnel endpoints. OpenDrop attempts to automate this with a minimal signaling server and peer-to-peer data channels.

What This Does

OpenDrop is a two-part system totaling 12 files. The client (client/index.html, client/main.js, client/style.css, client/.gitignore) is a static ES-Modal app that manages the UI, PeerConnection setup, and DataChannel file transfer. The server (server/index.js, server/package.json, server/package-lock.json, server/.gitignore) is a lightweight Node.js WebSocket signaling service that matches devices with matching public IPs and handles file-upload links that expire in 24 hours.

Key entry points trace as follows:

  • startConnection (client/main.js:196) reaches 3 functions and is the primary execution start.
  • handleOffer (client/main.js:212), handleAnswer (client/main.js:222), and startSendingFile (client/main.js:294) follow from connection setup.
  • The server’s generateName, getClientIp, getPeers, sendToClient, and broadcastToNetwork (server/index.js) handle signaling message routing and CORS.

The internal call graph contains 46 resolved call edges. showToast is the most widely called function (10 call sites), followed by getOrCreateConnection (4), updateStatus (3), sendSignaling (3), and readSlice (3). ws.onmessage fans into addPeer, removePeer, handleOffer, handleAnswer, handleCandidate, and handleIncomingFileRequest, creating a fan-out pattern that fans into the highest blast-radius functions. The import graph has 2 internal modules with 0 edges and 0 circular dependencies, keeping the dependency graph flat but the runtime call graph dense.

File-transfer paths are peer-to-peer for same-network peers; files never touch the signaling server. For link-based sharing, the file uploads through the server and a download link is generated with a 24-hour TTL.

How It Is Wired

Execution starts at startConnection in client/main.js:196, which invokes connectSignaling to open a WebSocket to the signaling server. ws.onopen triggers updateStatus. Incoming messages on ws.onmessage fan into showToast (x2), addPeer, removePeer, handleOffer, handleAnswer, handleCandidate, or handleIncomingFileRequest. Peer connection establishment flows through pc.onicecandidate -> sendSignaling and pc.ondatachannel -> setupDataChannel. File chunks are read via readSlice and sent over the DataChannel. The server’s CORS configuration uses a wildcard (cors() in server/index.js), which combined with credential-inclusive requests creates a browser-enforced security risk.

How To Use It

Same-network transfer: Open OpenDrop on two devices on the same Wi-Fi. They auto-discover via the signaling server. Click a peer to initiate a direct WebRTC DataChannel transfer; files never pass through the server.

Share via Link: Click “Share via Link,” select a file, and a download link is generated. The file uploads through the signaling server; the link expires in 24 hours.

Hosting:

  • Frontend: This repo includes a GitHub Action that deploys client/ to GitHub Pages. Set the source to GitHub Actions in repository Settings > Pages.
  • Backend: Deploy to Koyeb or Render. Root Directory: server; Build Command: npm install; Start Command: node index.js. After deployment, replace SIGNALING_URL in client/main.js and push to main.

Local development:

cd server
npm install
node index.js
cd client
python3 -m http.server 8080

Navigate to http://localhost:8080.

Configuration: the only required config is SIGNALING_URL in client/main.js, set to the deployed or local signaling server URL.

Real-World Use

Two developers on a coffee-shop Wi-Fi need to share a 200 MB design mockup. They open OpenDrop on their laptops; the peers auto-detect, establish a WebRTC DataChannel, and the file transfers directly at LAN speeds without touching any external server. Later, one sends a contract to a client via the “Share via Link” flow: the file uploads to the hosted signaling server, and the client receives a time-limited download link, eliminating the need for a separate file-hosting service.

Code Health & Issues

Static analysis produced the following measured findings:

  • [MEDIUM/cognitive_load] Deep nesting in server/index.js – max indentation depth 6; control flow is hard to follow. Fix: flatten with early returns/guard clauses.
  • [HIGH] Wildcard CORS origin in server/index.js – cors() allows any origin. Combined with credentials, any page can make authenticated requests without user consent, a configuration browsers explicitly reject. Fix: replace wildcard with an explicit allow list of served origins.
  • [MEDIUM] No Dependabot or Renovate configured – 1 manifest present, no update bot. Without automated dependency updates, known vulnerabilities remain unpatched until manual audit.
  • [MEDIUM] No dependency vulnerability scan gates pull requests – CI (GitHub Actions) runs but lacks dependency-review-action or osv-scanner. A known-vulnerable package could reach production without detection.
  • [LOW] Workflow job timeouts absent in .github/workflows/deploy.yml – the six-hour platform default means wedged steps can overlap behind a two-hourly schedule.
  • No test files detected repository-wide – untested code paths, including the WebRTC signaling and file-transfer logic.
  • CI present (GitHub Actions), license present (proprietary per LICENSE), lockfile present (server/package-lock.json), no committed secrets found.

The Bottom Line

OpenDrop delivers a functional, zero-dependency P2P file-transfer experience for local networks with a simple link-sharing fallback. The vanilla JS/ESM client and lightweight Node signaling server are easy to deploy and require no build pipeline. However, the wildcard CORS policy is a real security misconfiguration, the codebase has zero test coverage, and the CI pipeline offers no dependency-vulnerability gate. It’s well-suited for individuals, teams, or short-term workflows where quick, free P2P transfer outweighs the need for hardened operations, but the CORS fix, test additions, and CI dependency scanning should be prioritized before any deployment beyond a trusted local network.