dev-sync keeps local development directories mirrored to remote SFTP destinations. It is built for the common workflow where you edit locally, run a background daemon, and need a remote machine to stay current without manually copying files after every change.
Sync is one-way only: local to remote. Multiple sync pairs run concurrently in a single process.
- Interactive onboarding (
dev-sync init) — prompts for connection details and verifies the remote directory exists before saving - Verifies SFTP host keys using
~/.ssh/known_hosts;initcan prompt to trust an unknown host - Multiple sync pairs run concurrently from one daemon
- Initial sync on startup, then real-time updates via filesystem events
- Debounced reconciliation — editor save-storms collapse into one accurate log line per change
- Finite retry with exponential backoff for transient pair failures
- Respects
.gitignore; skips.git/directories - Structured JSON logging to a file;
dev-sync logsfor a pretty, recent view - Background daemon with proper start/stop/status lifecycle
brew install --cask benkleyner/tap/dev-syncRelease tags publish darwin/linux binaries and update the Homebrew tap via GoReleaser.
Configure a local project and remote SFTP directory once:
dev-sync initThen run the sync daemon in the background:
dev-sync start
dev-sync status
dev-sync logsAdd more sync pairs by running dev-sync init again. Stop the daemon with:
dev-sync stopRequires Go 1.25 or newer.
go build -o dev-sync .
go test ./...
go vet ./...Or run without building: go run . <command>.
| Command | What it does |
|---|---|
init |
Interactive setup for a new sync pair. Verifies SFTP creds and remote dir. |
list |
Show configured sync pairs. |
run |
Run all pairs in the foreground (logs to terminal, Ctrl-C to stop). |
start |
Launch the daemon in the background. |
stop |
Stop the running daemon. |
status |
Report whether the daemon is running. |
logs [n] |
Print the last n log entries (default 50). |
scan <dir> |
List every file under <dir>, respecting .gitignore. Debug helper. |
mirror <src> <user@host:dir> |
One-off SFTP mirror without using config. Requires DEV_SYNC_PASSWORD env var. |
version |
Print the version. |
run and start execute the same syncing logic. run is for interactive development (logs in your terminal); start is for set-and-forget background operation.
Each daemon-managed sync pair retries transient connection, initial sync, or watch-loop failures up to 10 times with exponential backoff capped at 60 seconds. Host key verification remains strict: unknown, changed, or revoked host keys fail closed and count against the retry limit.
dev-sync uses the standard OpenSSH ~/.ssh/known_hosts file. During dev-sync init, an unknown SFTP host key is shown with its key type and SHA256 fingerprint, and you can choose whether to add it to known_hosts. Runtime commands (run, start, and mirror) do not prompt; unknown, changed, or revoked host keys fail closed.
Config, pidfile, and log file live under your platform's user config directory:
- macOS:
~/Library/Application Support/dev-sync/ - Linux:
~/.config/dev-sync/
Contents:
config.json— sync pairs and keychain password references (created byinit)daemon.pid— written onstart, removed onstopdaemon.log— JSON-lines log written by the background daemon
The config file is created with 0600 permissions; the directory with 0700.
SFTP passwords are stored in the OS keychain via go-keyring, and legacy plaintext passwords are migrated the next time init, list, run, or start loads the config.
Issues and pull requests are welcome. See CONTRIBUTING.md for local setup and contribution guidance. Please report security-sensitive issues through the process in SECURITY.md.
- One-way sync only. Remote-side changes are not detected and may be overwritten on the next local change.
- No nested
.gitignoresupport. Only the root.gitignoreis consulted. - Background daemon control is Unix-only.
start/stop/statusare disabled on Windows.