Dropbox-style folder sync for your own machines. Pick a folder, pair with a 6-character code, and it stays in sync — peer-to-peer, no cloud account.
A macOS menu-bar app, plus a headless syncbox CLI for Linux servers.
- Watches one folder per device; adds, edits, and deletes propagate in seconds.
- Direct device-to-device transfer (same Wi-Fi = fast, no internet hop).
- Conflicts keep both copies — the loser becomes
name.conflict-<host>-<timestamp>.ext. - Deletes propagate everywhere. Exception: a file edited after the delete wins and comes back (last-write-wins).
- File content is streamed, so large files don't blow up memory.
Each file is one entry in an iroh-docs CRDT document
(key = relative/path, content addressed by BLAKE3). iroh handles the
networking — QUIC transport, NAT hole-punching, encryption — and iroh-blobs
moves the content. syncbox mirrors the document to disk and back.
Built on Rust, Tauri 2 (menu-bar app), the iroh stack (P2P + sync), notify (file watching), and tokio. Pairing uses a tiny Cloudflare Worker.
crates/syncbox-core/ sync engine (shared)
crates/syncbox-cli/ headless CLI
src-tauri/ macOS menu-bar app
src/ app window UI (HTML/CSS/JS)
pair-server/ Cloudflare Worker — 6-char pairing codes
Needs Rust. The GUI also needs Bun.
Optionally bake in your pairing-server host (file is gitignored):
echo "https://pair.<your-domain>" > pair-server.txtmacOS app:
bun install
bun run tauri build # → target/release/bundle/macos/Unsigned — on first launch, right-click the app → Open → Open.
CLI:
cargo build --release -p syncbox-cli # → target/release/syncboxGUI: Choose folder, click Get code on one Mac, type it into Use code on the other.
CLI:
syncbox init ~/Sync # both devices
syncbox pair # device A — prints a code
syncbox join ABC-123 # device B
syncbox run # both — or via systemd, see crates/syncbox-cli/syncbox.serviceThe 6-character code is a short-lived swap, not a connection:
- Device A uploads its iroh ticket (public connection info) to the pairing server and gets back a 6-char code with a 5-minute TTL.
- Device B redeems the code for the ticket; the server then forgets it — single use.
- B connects to A directly over iroh (direct → hole-punch → relay). From here the pairing server is out of the loop.
The server only ever holds the ticket — public metadata, never file content.
No central server for your data. Two optional pieces, both yours to run.
Pairing server — pair-server/ is a Cloudflare Worker:
cd pair-server
bun install
bunx wrangler login
bunx wrangler kv namespace create PAIR # paste the id into wrangler.toml
# set the routes pattern in wrangler.toml to pair.<your-domain>
bunx wrangler deployPoint devices at it — resolved highest-priority-first: the SYNCBOX_PAIR_SERVER
env var, the app's Advanced → Pair server URL field, or the compiled-in
default from pair-server.txt. It is two endpoints (POST /pair,
GET /pair/<code>), so you can reimplement it in anything — see
pair-server/README.md.
Relay (optional) — iroh uses n0's public relays for NAT traversal; they
see connection metadata, never content. Run your own
iroh-relay to avoid that.
Per device, under ~/Library/Application Support/dev.syncbox/ (override with
the SYNCBOX_DATA_DIR env var):
config.json— folder, device name, ticket, known peersiroh/— secret key, blob store, docs database
Lightly tested — keep backups of anything important.
- Pre-release dependency — pinned to
iroh 1.0.0-rc.0; upgrades may need code changes. - No delta sync — editing one byte of a 1 GB file re-sends the gigabyte.
- No atomic snapshots — app bundles (
.alfredpreferences, SQLite+WAL) can sync half-updated. Individual files write atomically; bundles don't. - Deletes are real — no trash, no undo. Recover from a backup.
- Last-write-wins — simultaneous edits don't merge; clock skew can pick the wrong winner.
- Empty folders don't sync — only files are entries.
- One folder, one peer set — no multi-folder or selective sync.
- Unsigned app, no auto-updates.
- Blob store is not encrypted at rest — on-wire traffic is encrypted, the
local
iroh/directory is not.
bun run tauri dev # GUI
cargo run -p syncbox-cli -- status # CLIMIT. See LICENSE.