Personal Project · 2026
Syncer: A Lightweight Personal Sync Backend
A rough prototype built to scratch a specific itch: syncing local-first apps across my own devices without handing data to a third-party service or spinning up infrastructure I'd have to maintain.
How It Started
I'd been building local-first browser tools — Brew Log being the main one — and kept hitting the same wall: everything worked great on one device, then I'd want to check something from my phone and the data wasn't there. The obvious solutions (Firebase, Supabase, any SaaS sync layer) all involved handing my data to a service and adding ongoing costs or account management overhead I didn't want.
I didn't need real-time sync. I didn't need conflict resolution. I needed a simple way to push a JSON blob from one device and pull it on another. That's a solved problem — I just needed to solve it for myself, on my own terms.
What I Built
Syncer is a Cloudflare Worker that stores a single JSON blob per app per "vault." A vault is identified by a passphrase the user chooses once — SHA-256 hashed client-side before it ever leaves the browser, so the server stores only an opaque key and never sees the raw credential.
The client is a 40-line drop-in module — sync-client.js — that any vanilla JS app can adopt with two calls: push(data) and pull(). That's the entire API. Sync is manual and last-write-wins, with a timestamp guard that prevents a stale device from overwriting newer data, and an automatic local backup before any remote data is applied.
The architecture assumes each user deploys their own Worker instance ("bring your own Cloudflare"). No shared infrastructure, no shared keys, complete data ownership. At personal-use volume it runs indefinitely on Cloudflare's free tier — roughly $0/month.
Brew Log was the first consumer. It integrates via a feature flag so the sync UI is invisible to public visitors while I get the multi-device behavior I actually wanted.
Key Decisions
Cloudflare Workers + KV over Supabase or Firebase. No cold starts, global edge distribution, and genuinely free at low volume. More importantly: no account required for the data owner. The worker is infrastructure I control and can delete — there's no vendor relationship to manage.
Client-side hashing. The passphrase never leaves the browser in plaintext. The server sees only a SHA-256 hash used as a KV key. This simplifies the trust model considerably — there's nothing sensitive to protect on the server side because the server never receives anything sensitive.
Manual push/pull over auto-sync. Explicit control avoids surprise overwrites. The timestamp guard prevents the most obvious foot-gun (syncing from a stale device), but the overall model is intentionally simple. For low-volume, occasional-use patterns, manual sync is the right call — it puts the user in control of when data moves.
Drop-in module design. The client module is small enough to paste into any project. Adoption is two function calls. The goal was zero friction for adding sync to a new vanilla JS app — Brew Log proved the pattern works, and future projects can pick it up without rearchitecting anything.
What It Is (and Isn't)
This is a rough prototype built to address a personal headache. It's not a product, not a library, not a framework. The "bring your own Cloudflare" model means it's not something you can just hand to someone else and have them use in 5 minutes — there's a deploy step involved.
Last-write-wins is a deliberate simplification that works for my use case (one person, a few devices, infrequent syncs) and would be wrong for almost any multi-user scenario. The timestamp guard is a basic safeguard, not a real conflict resolution strategy.
It does exactly what I needed it to do. Whether it grows into something more general depends on whether I run into cases where the current model breaks down.