A ground-up rebuild of how FileSync connects and saves files — native WebRTC, a built-in signaling server, and true streaming-to-disk for transfers of any size.
This is a major release. FileSync no longer depends on PeerJS: connection setup is now handled by a lightweight first-party signaling server bundled with the app, and received files stream straight to disk so even multi-gigabyte transfers stay memory-safe.
Important
Upgrading from 3.x requires re-downloading the compose files (docker-compose.yml, and for HTTPS the docker-compose-ssl.yml + Caddyfile). Pulling the new image alone is not enough — see Upgrading.
✨ Highlights
- 🔗 Native WebRTC — PeerJS is gone. Smaller footprint, fewer moving parts, no third-party connection broker.
- 📡 Built-in signaling server — served by FileSync itself at
/ws. Nothing extra to run; it relays only connection setup and never sees your files. - 💾 Streaming downloads, any size — files are written to disk as they arrive, so memory stays flat even on multi-GB transfers.
- 🩺 Redesigned diagnostics page at
/test.html— light/dark, an at-a-glance verdict, and a one-click connectivity report to share with support. - 🛡️ Hardened for production — required secret at startup, rate-limited credentials, a self-healing container, and self-hosted fonts.
🚀 What's new
Connectivity
- Native
RTCPeerConnection/RTCDataChannelclient replacing the PeerJS wrapper. - First-party WebSocket signaling relay (
/ws) with rate limiting, idle timeouts, and capacity guards. - Relayed transfers now work correctly behind Docker — TURN relay candidates are rewritten in transit so peers on different networks get a reachable address.
Transfers
- Chunked, backpressured protocol that keeps both sender and receiver memory bounded.
- Three save methods, auto-selected per browser:
Method When Notes File System Access Desktop Chrome/Edge/Brave/Opera (HTTPS) Streams straight to a file you pick Service Worker All modern browsers (HTTPS) Streamed browser download, memory-safe Blob Fallback / plain HTTP Buffered in memory (~500 MB practical limit)
Reliability
- Serialized disk writes — fixes a File System Access corruption case on fast/large transfers.
- Transfers no longer hang if a peer drops mid-send; abort, remove, and "cancel all" now tear down cleanly (no leaked connections or stuck downloads).
- Transient network blips are tolerated instead of killing an in-progress transfer.
- The container self-heals: if the backend process dies, it restarts instead of serving a half-dead app.
Security
- The server refuses to start without
SECRET_KEY(used to sign TURN credentials and tokens). /api/credentialsis rate-limited; CORS is off by default (opt-in viaCORS_ORIGINS).- Fonts are self-hosted — no third-party font CDN, satisfying the app's strict Content-Security-Policy.
Tooling & docs
- End-to-end Playwright test harness covering browsers × save methods × connection modes.
- Dependabot for Python, Docker, GitHub Actions, and the test harness.
- README refreshed: how connections are made, the required ports, and how files are saved.
⚠️ Upgrading from 3.x
The compose files changed — re-download them.
-
Re-download the deploy file(s) for your setup:
- HTTP →
deploy/docker-compose.yml - HTTPS →
deploy/docker-compose-ssl.yml+deploy/Caddyfile
- HTTP →
-
Set your secret (now required — the app won't start without it). Generate one:
python3 -c "import secrets, base64; print(base64.b64encode(secrets.token_bytes(32)).decode())"Replace both
<SECRET_KEY>placeholders in the compose file. -
Open the required ports (below) if you haven't already.
-
Pull and restart:
docker compose pull && docker compose up -d
💡 No separate PeerJS server is needed anymore — if you ran one, you can decommission it.
📋 Required ports
| Port | Protocol | Purpose |
|---|---|---|
80 / 443 |
TCP | Web interface (HTTP / HTTPS) |
3478 |
TCP + UDP | STUN/TURN — connection setup |
50000–50100 |
UDP | TURN relay range (used when a direct path isn't possible) |
Full changelog: v.3.7.0...v.4.0.0