|
2 | 2 | <img src="web/assets/icon.png" alt="FileSync Logo" width="80"> |
3 | 3 | <h1 align="center">FileSync</h1> |
4 | 4 |
|
5 | | -**Send files from one device to many in real-time** |
| 5 | +**Send files from one device to many, in real time — private, peer-to-peer, with no size limit.** |
6 | 6 |
|
7 | 7 | <p align="center"> |
8 | | -<a href="https://github.com/polius/filesync/actions/workflows/release.yml"><img src="https://github.com/polius/filesync/actions/workflows/release.yml/badge.svg"></a> <a href="https://github.com/polius/filesync/releases"><img alt="GitHub Release" src="https://img.shields.io/github/v/release/polius/filesync"></a> <a href="https://hub.docker.com/r/poliuscorp/filesync"><img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/poliuscorp/filesync"></a> |
| 8 | +<a href="https://github.com/polius/filesync/actions/workflows/release.yml"><img src="https://github.com/polius/filesync/actions/workflows/release.yml/badge.svg"></a> <a href="https://github.com/polius/filesync/releases"><img alt="GitHub Release" src="https://img.shields.io/github/v/release/polius/filesync"></a> <a href="https://hub.docker.com/r/poliuscorp/filesync"><img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/poliuscorp/filesync"></a> <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg"></a> |
9 | 9 | </p> |
10 | 10 |
|
11 | 11 | <br> |
12 | 12 |
|
13 | | -<p align="center"> |
14 | | -<b>FileSync</b> is a file sharing web application that allows users to transfer files between multiple devices with end-to-end encryption. |
15 | | -</p> |
| 13 | + |
16 | 14 |
|
17 | | -<br> |
| 15 | +</div> |
18 | 16 |
|
19 | | - |
| 17 | +## Features |
20 | 18 |
|
21 | | -</div> |
| 19 | +- 🔒 **Private by design** — files travel directly between browsers over encrypted WebRTC. The server only brokers the initial handshake; it never sees your file contents. |
| 20 | +- 🚀 **No size limit** — received files stream straight to disk, so even multi-gigabyte transfers use almost no memory (over HTTPS — see [how files are saved](#how-received-files-are-saved)). |
| 21 | +- 👥 **One-to-many** — share a room link or QR code and send to as many devices at once. |
| 22 | +- 🌐 **Works across networks** — connects directly when possible, with automatic STUN/TURN relay fallback for restrictive NATs and firewalls. |
| 23 | +- 🪄 **No installs, no accounts** — recipients just open a link in any modern browser. Optional password protection per room. |
| 24 | +- 🐳 **Self-hosted** — a single Docker image you run yourself. |
22 | 25 |
|
23 | | -# Installation |
| 26 | +## Table of contents |
24 | 27 |
|
25 | | -## Prerequisites |
| 28 | +- [Features](#features) |
| 29 | +- [Table of contents](#table-of-contents) |
| 30 | +- [How to use](#how-to-use) |
| 31 | +- [Self-hosting](#self-hosting) |
| 32 | + - [Prerequisites](#prerequisites) |
| 33 | + - [Option A — HTTP (local network / quick start)](#option-a--http-local-network--quick-start) |
| 34 | + - [Option B — HTTPS (public domain, recommended)](#option-b--https-public-domain-recommended) |
| 35 | + - [Stopping FileSync](#stopping-filesync) |
| 36 | +- [Required ports](#required-ports) |
| 37 | +- [Customizing ports](#customizing-ports) |
| 38 | +- [Configuration](#configuration) |
| 39 | +- [How received files are saved](#how-received-files-are-saved) |
| 40 | +- [Under the hood](#under-the-hood) |
| 41 | +- [License](#license) |
26 | 42 |
|
27 | | -- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) installed |
28 | | -- Python 3 (for generating the secret key) |
| 43 | +## How to use |
29 | 44 |
|
30 | | -## Quick Start |
| 45 | +1. **Open** your FileSync URL — you land in a room with a unique link and a QR code. |
| 46 | +2. **Share** the link (or QR) with the people or devices you want to send to. |
| 47 | +3. **Drop in your files** — drag and drop them (or click **Send Files**). Recipients see them appear and can download instantly. |
31 | 48 |
|
32 | | -### Option 1: HTTP (Local Development) |
| 49 | +> Want to restrict access? Click **Add password** to protect the room before sharing the link. |
33 | 50 |
|
34 | | -**1. Download the required files** |
| 51 | +## Self-hosting |
35 | 52 |
|
36 | | -Get [docker-compose.yml](deploy/docker-compose.yml) from the `deploy` folder. |
| 53 | +### Prerequisites |
37 | 54 |
|
38 | | -**2. Generate a secret key** |
| 55 | +- [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) |
| 56 | +- Python 3 (only to generate the secret key below) |
39 | 57 |
|
40 | | -Run the following command to generate a secure 32-byte base64-encoded secret: |
| 58 | +Every deployment needs a **secret key** (it signs the TURN credentials used for NAT traversal). Generate one with: |
41 | 59 |
|
42 | 60 | ```bash |
43 | 61 | python3 -c "import secrets, base64; print(base64.b64encode(secrets.token_bytes(32)).decode())" |
44 | 62 | ``` |
45 | 63 |
|
46 | | -**3. Configure the secret** |
47 | | - |
48 | | -Open `docker-compose.yml` and replace **both occurrences** of `<SECRET_KEY>` with the generated value. |
49 | | - |
50 | | -Example: |
51 | | -```yaml |
52 | | -... |
53 | | -- --static-auth-secret=/RaFOHJQQPAAXRNdaDhfBghvX9+o9UJEazKgIopK3TI= |
54 | | -... |
55 | | -- SECRET_KEY=/RaFOHJQQPAAXRNdaDhfBghvX9+o9UJEazKgIopK3TI= |
56 | | -... |
57 | | -``` |
58 | | - |
59 | | -**4. Start FileSync** |
60 | | - |
61 | | -```bash |
62 | | -docker-compose up -d |
63 | | -``` |
| 64 | +> ⚠️ Use your **own** generated value — never ship the examples shown below. |
64 | 65 |
|
65 | | -Access FileSync at `http://localhost:80` |
| 66 | +### Option A — HTTP (local network / quick start) |
66 | 67 |
|
67 | | -### Option 2: HTTPS (Production with Custom Domain) |
| 68 | +Best for trying FileSync or running it on a trusted LAN. Note that large transfers (>~500 MB) are unreliable over plain HTTP — for those, use [Option B](#option-b--https-public-domain-recommended). |
68 | 69 |
|
69 | | -**1. Download the required files** |
| 70 | +**1. Download** [`deploy/docker-compose.yml`](deploy/docker-compose.yml). |
70 | 71 |
|
71 | | -Get [docker-compose-ssl.yml](deploy/docker-compose-ssl.yml) and [Caddyfile](deploy/Caddyfile) from the `deploy` folder. |
| 72 | +**2. Set your secret.** Replace **both** `<SECRET_KEY>` placeholders with your generated value: |
72 | 73 |
|
73 | | -**2. Generate a secret key** |
| 74 | +```yaml |
| 75 | +# (use your own generated value — these are illustrative) |
| 76 | +- --static-auth-secret=Hs9k…your-generated-key…= |
| 77 | +- SECRET_KEY=Hs9k…your-generated-key…= |
| 78 | +``` |
74 | 79 |
|
75 | | -Run the following command to generate a secure 32-byte base64-encoded secret: |
| 80 | +**3. Start it:** |
76 | 81 |
|
77 | 82 | ```bash |
78 | | -python3 -c "import secrets, base64; print(base64.b64encode(secrets.token_bytes(32)).decode())" |
| 83 | +docker compose up -d |
79 | 84 | ``` |
80 | 85 |
|
81 | | -**3. Configure the secret** |
| 86 | +Open **`http://localhost`** (or your server's IP). |
82 | 87 |
|
83 | | -Open `docker-compose-ssl.yml` and replace **both occurrences** of `<SECRET_KEY>` with the generated value. |
| 88 | +### Option B — HTTPS (public domain, recommended) |
84 | 89 |
|
85 | | -Example: |
86 | | -```yaml |
87 | | -... |
88 | | -- --static-auth-secret=/RaFOHJQQPAAXRNdaDhfBghvX9+o9UJEazKgIopK3TI= |
89 | | -... |
90 | | -- SECRET_KEY=/RaFOHJQQPAAXRNdaDhfBghvX9+o9UJEazKgIopK3TI= |
91 | | -... |
92 | | -``` |
| 90 | +Caddy obtains and renews a Let's Encrypt certificate automatically. HTTPS also unlocks memory-safe streaming for files of any size. |
93 | 91 |
|
94 | | -**4. Configure your domain** |
| 92 | +**1. Download** [`deploy/docker-compose-ssl.yml`](deploy/docker-compose-ssl.yml) and [`deploy/Caddyfile`](deploy/Caddyfile). |
95 | 93 |
|
96 | | -Open `Caddyfile` and replace `yourdomain.com` with your actual domain. |
| 94 | +**2. Set your secret.** Replace **both** `<SECRET_KEY>` placeholders (same as Option A). |
97 | 95 |
|
98 | | -Example: |
99 | | -``` |
100 | | -filesync.app { |
101 | | - reverse_proxy filesync:80 |
| 96 | +**3. Set your domain.** In `Caddyfile`, replace `yourdomain.com` with your domain: |
| 97 | + |
| 98 | +```caddyfile |
| 99 | +filesync.example.com { |
| 100 | + reverse_proxy filesync:80 |
102 | 101 | } |
103 | 102 | ``` |
104 | 103 |
|
105 | | -**5. Start FileSync** |
| 104 | +**4. Start it:** |
106 | 105 |
|
107 | 106 | ```bash |
108 | | -docker-compose -f docker-compose-ssl.yml up -d |
| 107 | +docker compose -f docker-compose-ssl.yml up -d |
109 | 108 | ``` |
110 | 109 |
|
111 | | -Caddy will automatically obtain and manage SSL certificates from Let's Encrypt. |
| 110 | +Open **`https://yourdomain.com`**. |
112 | 111 |
|
113 | | -Access FileSync at `https://yourdomain.com` |
114 | | - |
115 | | -## Stopping FileSync |
| 112 | +### Stopping FileSync |
116 | 113 |
|
117 | 114 | ```bash |
118 | | -# For HTTP setup |
119 | | -docker-compose down |
120 | | - |
121 | | -# For HTTPS setup |
122 | | -docker-compose -f docker-compose-ssl.yml down |
| 115 | +docker compose down # HTTP setup |
| 116 | +docker compose -f docker-compose-ssl.yml down # HTTPS setup |
123 | 117 | ``` |
124 | 118 |
|
125 | | -## Required Ports |
126 | | - |
127 | | -To expose FileSync to the internet, ensure the following ports are open on your server/firewall: |
128 | | - |
129 | | -### HTTP Setup |
130 | | -- **Port 80** (TCP) - Web interface |
131 | | -- **Port 3478** (TCP + UDP) - STUN/TURN server for WebRTC |
| 119 | +## Required ports |
132 | 120 |
|
133 | | -### HTTPS Setup |
134 | | -- **Port 443** (TCP) - Web interface (HTTPS) |
135 | | -- **Port 3478** (TCP + UDP) - STUN/TURN server for WebRTC |
| 121 | +Open these on your server/firewall to expose FileSync to the internet: |
136 | 122 |
|
137 | | -> **Note:** Port 3478 is essential for establishing peer-to-peer connections, especially when devices are behind NAT/firewalls. |
| 123 | +| Port | Protocol | Purpose | |
| 124 | +|---|---|---| |
| 125 | +| `80` *(HTTP)* / `443` *(HTTPS)* | TCP | Web interface | |
| 126 | +| `3478` | TCP + UDP | STUN/TURN — peer-to-peer connection setup | |
| 127 | +| `50000–50100` | UDP | TURN relay range — used when a direct connection isn't possible | |
138 | 128 |
|
139 | | -## Customizing Ports |
| 129 | +> Port `3478` handles connection setup. The `50000–50100` UDP range carries relayed traffic for the ~5–10% of connections that can't go direct (typically a peer behind symmetric NAT or a UDP-blocking firewall). |
140 | 130 |
|
141 | | -### Changing the HTTP Port |
| 131 | +## Customizing ports |
142 | 132 |
|
143 | | -By default, FileSync uses port 80. To use a different port (e.g., 8080): |
144 | | - |
145 | | -**1. Edit `docker-compose.yml`** |
146 | | - |
147 | | -Find the `ports` section under the `filesync` service: |
| 133 | +**HTTP port.** In `docker-compose.yml`, change the **first** number of the `filesync` port mapping (the second is the internal container port — leave it as `80`): |
148 | 134 |
|
149 | 135 | ```yaml |
150 | 136 | ports: |
151 | | - - "80:80" |
| 137 | + - "8080:80" # serve on http://localhost:8080 |
152 | 138 | ``` |
153 | 139 |
|
154 | | -Change the **first** port number to your desired port: |
| 140 | +**HTTPS port.** Keep Caddy on `443` and put your own reverse proxy (Nginx, Traefik, standalone Caddy) in front if you need a non-standard external port — terminate TLS there and forward to FileSync's internal HTTP port. |
155 | 141 |
|
156 | | -```yaml |
157 | | -ports: |
158 | | - - "8080:80" |
159 | | -``` |
| 142 | +## Configuration |
160 | 143 |
|
161 | | -**2. Access FileSync** |
| 144 | +Environment variables on the `filesync` service: |
162 | 145 |
|
163 | | -Access FileSync at `http://localhost:8080` (or your server IP with the new port). |
| 146 | +| Variable | Required | Default | Description | |
| 147 | +|---|---|---|---| |
| 148 | +| `SECRET_KEY` | **Yes** | — | Signs TURN credentials and tokens. Must match the coturn `--static-auth-secret`. The app won't start without it. | |
164 | 149 |
|
165 | | -> **Note:** The second port number (80) should remain unchanged as it refers to the internal container port. |
| 150 | +## How received files are saved |
166 | 151 |
|
167 | | -### Changing the HTTPS Port |
| 152 | +FileSync writes received files to disk as the bytes arrive, so transfers use almost no memory regardless of size. It uses the first method the browser supports, falling back in order: |
168 | 153 |
|
169 | | -To use a custom external port, deploy a separate reverse proxy (Nginx, Traefik, or standalone Caddy) that: |
170 | | -- Listens on port 443 with SSL termination |
171 | | -- Forwards traffic to FileSync on your custom internal HTTP port |
| 154 | +1. **File System Access API** — streams straight to a file you pick. Desktop Chromium browsers (Chrome, Edge, Brave, Opera) over HTTPS. |
| 155 | +2. **Service Worker** — streams into a normal browser download. All modern browsers over HTTPS. |
| 156 | +3. **Blob** — buffers the whole file in memory, then saves it. Last resort; the only option over plain HTTP, and unreliable past ~500 MB. |
172 | 157 |
|
173 | | -This approach maintains standard HTTPS on port 443 while allowing flexible internal port configuration. |
| 158 | +The first two require a secure context (HTTPS, or `localhost`), so **serving FileSync over HTTPS is recommended** — it enables memory-safe transfers of any size. |
174 | 159 |
|
175 | 160 | ## Under the hood |
176 | 161 |
|
177 | | -FileSync uses [PeerJS](https://github.com/peers/peerjs) (a WebRTC wrapper) to transfer files between multiple devices. Files shared are peer-to-peer, which means there is a direct file transfer between the sender and receiver without any intermediate server. Your files remain private and secure throughout the entire transfer process. |
| 162 | +FileSync uses native [WebRTC](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API) to transfer files directly between devices — peer-to-peer, with no intermediate server in the data path. Your files stay private throughout. |
| 163 | + |
| 164 | +A lightweight WebSocket signaling server (served at `/ws` by the FileSync app itself) assists only with the initial connection setup — relaying SDP offers/answers and ICE candidates between peers. Once a peer-to-peer connection is established, the signaling server steps back and file bytes flow directly between browsers. **The server never has access to file contents.** |
| 165 | + |
| 166 | + |
| 167 | + |
| 168 | +## License |
178 | 169 |
|
179 | | - |
| 170 | +Released under the [MIT License](LICENSE). |
0 commit comments