The Problem
Hosted sync services — Dropbox, Drive, iCloud — work well until they don't: storage tiers you didn't ask for, files quietly held hostage behind a renewal, telemetry baked into the client. The mechanics underneath are not complicated: a folder, a bucket, and a careful enough merge. The hard part is doing that merge honestly.
The Approach
DropBear synchronizes a local folder with any S3-compatible bucket. Storage is decoupled from sync, so the same binary works against Cloudflare R2, Backblaze B2, AWS S3, or a self-hosted MinIO — whichever is cheapest, closest, or most trusted that week. The bucket is never treated as a filesystem: it holds content-addressed (SHA-256) blobs, one JSON manifest per device, and per-device heads. Local state lives in SQLite; each folder carries its own identity in a .dropbear/ directory.
- Bidirectional sync with explicit tombstones for deletes — absence is never read as "delete this everywhere."
- Conflict files, not silent overwrites. When two devices touch the same file, both versions survive on disk and neither is published until you resolve it.
- Sync modes picked per root: two-way, upload-only, download-only, or archive-only.
- Single static binary. Drop it on a host, point it at a bucket, done.
Doing the merge honestly
The interesting engineering is in the failure modes, not the happy path.
- Removable-drive safety. A missing root reads as offline, never as a mass deletion — pull the USB drive and DropBear waits instead of propagating a delete storm.
- Crash-safe deletes. Tombstone garbage collection runs a journaled
PUT pending → PUT real → DELETE pending → commitlifecycle, and reconciles on startup from a crash between any two steps without burning a sequence number. - Drift detection.
statuscross-checks every manifest entry — local and peer — against the blobs actually present in the bucket, so a half-uploaded sync surfaces instead of silently corrupting a restore.
A daemon, not a cron job
dropbear daemon supervises any number of roots in a single process — one goroutine per root, each with a recursive fsnotify watcher (debounced) and an adaptive remote-head poller that backs off when idle and snaps back the moment a peer publishes. Signals get two-strike handling (first asks for a graceful drain, second forces it); a panic in one root is recovered and isolated so it can't take down the others. Everything is structured log/slog output with a versioned JSON envelope.
Seeing what it's doing
The daemon exposes a loopback-only status API on 127.0.0.1, gated by a crypto/rand bearer token handed off through a 0600 runtime file. Two clients consume it: a one-page web dashboard embedded in the binary, and a native macOS menu-bar app. The core dropbear binary stays pure-Go and CGo-free — all the CGo needed for the tray is confined to its own command.


Online-only files
Not every device wants every byte. DropBear supports materialized views: a file can be a zero-byte stub (marked with an xattr) that stands in for remote content until you ask for it. shelve turns local files back into stubs after verifying the blob is reachable; fetch materializes them on demand. It's the "online-only file" pattern from the hosted services — but built on plain xattrs and the existing content-addressed store, with no kernel extension or proprietary filesystem.
Where it stands
A daily driver for my own cross-device sync, well past the prototype stage but pre-1.0. The CLI, multi-root daemon, status API, web dashboard, and menu-bar app all work today; the daemon self-installs as a systemd or launchd service. What's intentionally not done yet: the tray's start/stop toggle is still a stub, and there's no packaged installer or signed app bundle — installation is build-it-yourself for now. The current focus is promoting the read-only status API into a small local control plane so the dashboard and tray can drive sync, fetch, and shelve directly. Single-user, self-hosted, and no telemetry remain non-negotiable by design.