dotkeeper

Keep every machine in sync

Real-time P2P sync for code, dotfiles, and notes — with git history underneath. One binary, no central server, no cloud account.

dotkeeper — onboarding a fresh machine
$ dotkeeper init --name laptop --slot 0
[dotkeeper] machine: laptop (slot 0)
[dotkeeper] Syncthing identity generated
[dotkeeper] device ID: KFZZ7TP-GH5OLNL-WNSTHWQ-...

$ dotkeeper peer add desktop WB25TET-...
[dotkeeper] peer added: desktop

$ dotkeeper offers
PEER     REPO                          STATUS         ACTION
desktop  github.com/me/dotfiles        not subscribed dotkeeper subscribe github.com/me/dotfiles
desktop  github.com/me/notes           not subscribed dotkeeper subscribe github.com/me/notes
desktop  github.com/me/code            not subscribed dotkeeper subscribe github.com/me/code

$ dotkeeper subscribe github.com/me/dotfiles
✓ subscribed: github.com/me/dotfiles
  local path: (mirror convention — first scan_root + basename)
  next reconcile picks it up; force with `dotkeeper reconcile`

$ dotkeeper reconcile
Plan:
  + accept subscription github.com/me/dotfiles (mount ~/Documents/GitHub/dotfiles)

How it works

1

Initialize

dotkeeper init on your first machine. Generates a Syncthing identity, scaffolds machine.toml.

2

Pair peers

Exchange device IDs between machines via machine.toml or dotkeeper peer add. P2P mesh — no central hub.

3

Subscribe

dotkeeper offers shows what peers have. dotkeeper subscribe github.com/foo/repo opts in. Files arrive in seconds.

Features

One-command onboarding

dotkeeper subscribe github.com/foo/repo on a fresh machine. The mesh seeds the working tree from any peer that has it. No manual git clone + track dance.

Self-tuning multi-transport

Syncthing, git push over SSH, and Mutagen coexist. A cost model picks per change and learns from every transfer. Active benchmarking refines per-folder predictions in the background.

Zero infrastructure

No cloud server, no VPN, no SSH tunnel required. Machines find each other via Syncthing's open discovery network and connect peer-to-peer.

Single binary

Syncthing is compiled into the dotkeeper binary. Download, run, done. No separate Syncthing installation, no conflicts with an existing setup.

Git history

Scheduled git auto-commit and push. Every change is versioned. Roll back to any point in time. Works with your existing remotes.

Conflict-free by design

Syncthing handles real-time sync. Git timers are staggered per machine (slot offsets) so pushes never collide.

Cross-platform

Linux, macOS, Windows, FreeBSD, OpenBSD, NetBSD. Platform-native service management: systemd, launchd, Task Scheduler, cron.

Privacy-first

No telemetry, no crash reporting, no usage tracking. Syncthing's full network stack for connectivity, with all reporting disabled.

Performance is a fail criterion

Idle steady-state CPU sits at ~0%. Hot-path benchmarks are gated by CI — perf regressions can't ship. Cold-start scans spread across time so the daemon stays invisible to the dev experience.

Architecture

                        +-------------+
                        |  Machine A  |
                        +-------------+
                         /           \
                        /             \
                       v               v
            +-------------+   <--->   +-------------+
            |  Machine B  |           |  Machine C  |    ...
            +-------------+           +-------------+
                  \                       /
                   \                     /
                    v                   v
                     +-----------------+
                     |   git remote    |   <- staggered
                     +-----------------+      backup slots

  Per machine:  ~/.config/dotkeeper/machine.toml + state.toml
  Per repo:     <repo>/.dotkeeper.toml  (local-only opt-in signal)

Isolated Syncthing

Runs on its own ports (API: 18384, sync: 12000, discovery: 11027). Never touches your existing Syncthing installation.

Three-file config

machine.toml — declarative local config. state.toml — tool-owned runtime state. .dotkeeper.toml — local per-repo desired state.

Smart .stignore

Auto-generated ignore patterns: build artifacts, lockfiles, editor state, SQLite journals. Syncs what matters, skips what doesn't.

Install

Download binary

Grab the latest release for your platform:

Download from GitHub

macOS / Linux (Homebrew)

brew tap corbet-labs/dotkeeper
brew install dotkeeper

Arch Linux (AUR)

yay -S dotkeeper-bin

Build from source

git clone https://github.com/corbet-labs/dotkeeper
cd dotkeeper
make install

Requires Go 1.26+ and git.

Commands

dotkeeper initInitialize this machine
dotkeeper identityPrint this machine's name and device ID
dotkeeper startRun the daemon (Syncthing + reconciler)
dotkeeper reconcileConverge Syncthing and git now
dotkeeper statusShow full status
dotkeeper healthOperational dashboard with --explain, --watch, --json
dotkeeper doctorRun diagnostic checks
dotkeeper peer add/list/removeManage imperative peers
dotkeeper track <path>Register an out-of-root repo
dotkeeper untrack <path>Remove an out-of-root override
dotkeeper subscribe <git-url>Declare this machine wants the named repo (Phase 2)
dotkeeper unsubscribe <git-url>Remove an imperative subscription
dotkeeper subscriptions listShow merged subscription list with SOURCE column
dotkeeper offersList folders peers are advertising that this machine could subscribe to
dotkeeper conflict listList Syncthing sync-conflict files
dotkeeper conflict resolve-allAuto-resolve trivial conflicts (dedup + 3-way merge)
dotkeeper conflict keep <path>Keep current, drop the conflict variant
dotkeeper conflict accept <path>Replace current with the variant and commit
dotkeeper transport listList configured transports + availability
dotkeeper transport status [peer]Per-peer reachability + cross-repo cost-model parameters
dotkeeper transport reposPer-(transport, peer, repo) cost-model predictions
dotkeeper bench-nowOperator-triggered transport benchmark
dotkeeper bare-initConfigure peer-side git repos for direct git push
dotkeeper versionPrint version and commit