Peer-to-peer file transfer using WebRTC.
Files are transferred directly between browsers.
The backend is used only for signaling and session coordination.
No files pass through or are stored on the server.
-
A user opens the app and enters a display name.
-
The client establishes a WebSocket connection to the backend signaling server.
-
The server creates a session (WebSocket group) and the frontend generates a QR code containing the session URL.
-
A second user scans the QR code and joins the session.
-
WebRTC negotiation begins through the signaling server.
-
Once the peer connection is established, a WebRTC DataChannel is created.
-
Sender select a file to send to peer.
-
The receiver receives a popup request to accept the file offer.
-
Files are transferred in chunks through the WebRTC DataChannel into the browser's configured download directory.
Transfer speed is limited by the slowest network connection between the two peers.
| Component | Technology |
|---|---|
| Frontend | Angular |
| Backend | ASP.NET Core |
| Signaling | SignalR (WebSocket) |
| Transfer | WebRTC DataChannel |
- The backend only relays signaling messages (SDP and ICE candidates).
- File data never passes through the server.
- Transfers occur directly between peers.
Install dependencies and start dev server:
npm install
npx ng serveDevelopment uses the default local backend URL; no environment variable is needed.
Create a .env file in the project root (or configure via your hosting provider) with the production backend URL:
NG_APP_HUB_URL=https://your-production-server/hubThen build the app:
npm run build --productionThe frontend will use the value from the .env file to connect to the production backend.
Note: Security headers are configured at the server/hosting level and are platform-specific. The current setup uses
vercel.jsonfor Vercel. If migrating to a different host, recreate these headers in the appropriate config file.
Platform Config Location Vercel vercel.jsonNetlify netlify.tomlor_headersIIS web.configNginx nginx.confApache .htaccessASP.NET Program.csmiddlewareLocal dev angular.json→serve.options.headers
| Header | Value | Purpose | Details |
|---|---|---|---|
| Content-Security-Policy | see vercel.json |
Prevents XSS | Whitelists approved sources for scripts, styles, fonts and connections. Blocks anything not explicitly allowed. |
| X-Content-Type-Options | nosniff | Prevents MIME sniffing | Forces browser to trust the declared Content-Type header. Stops browser from guessing file types and potentially executing malicious code disguised as another file type. |
| Referrer-Policy | strict-origin-when-cross-origin | Protects user privacy | When a user navigates away from Whispr, only the domain is sent to the destination — never the full URL or any query parameters. |
| X-Frame-Options | DENY | Prevents clickjacking | Blocks other websites from embedding Whispr inside an iframe. Prevents attackers from overlaying invisible frames to trick users into unintended actions. |
- Meta tags and JSON-LD in
src/index.html
The gtag ID in src/index.html is tied to the original project's Google Analytics property. If you're forking or self-hosting, replace G-XXXXXXXXXX with your own Google Analytics Measurement ID or remove the gtag script entirely if you don't need analytics (see below).
<script async src="https://www.googletagmanager.com/gtag/js?id=G-MJG4NRL8JM"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'G-MJG4NRL8JM');
</script>