DropBear

A self-hosted file-sync daemon that pairs local folders with any S3-compatible bucket — with a daemon, web dashboard, and menu-bar app.

Kind

Storage Utility

Stack

Go, SQLite, S3

Status

beta

LAPTOP MOBILE USB DRIVE SERVER BUCKET S3 · R2 · B2 ›› CONTENT-ADDRESSED files keyed by hash — automatic deduplication TOMBSTONED DELETES removals propagate safely, never silently resurrect SAFE CONFLICTS two edits? both versions survive — no overwrite REMOVABLE DEVICES 1st-class USB & external drives, mounted or not TRANSPARENT LAYOUT files map 1:1, readable without DropBear

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.

Doing the merge honestly

The interesting engineering is in the failure modes, not the happy path.

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.

DropBear menu-bar app showing the daemon running, an online root, and an Open Dashboard action
Menu-bar app — daemon state, per-root status, one-click dashboard
DropBear web dashboard showing recent sync ticks, byte counts, and recent file changes for a root
Embedded dashboard — live ticks, transfer counts, recent changes

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.

Content-addressed SHA-256 blobs + JSON manifests on plain object storage
BYOB R2, B2, S3, MinIO — storage decoupled from sync
Daemon · Web · Tray Watches, syncs, and reports; CGo confined to the tray
Online-only files Materialized-view stubs that fetch on demand