Skip to content

The background agent

dotsync init installs a scheduled job that runs dotsync sync -q every 300 seconds. There’s no long-running daemon: each run starts, syncs, and exits. -q makes it silent, makes it exit at once if another sync is already running, and turns on desktop notifications.

Platform Mechanism Defined in
macOS launchd user agent {ref.defaults.agent_label} ~/Library/LaunchAgents/{ref.defaults.agent_label}.plist
Linux with a systemd user session user timer dotsync.timer ~/.config/systemd/user/dotsync.service and dotsync.timer
Linux without one a crontab line tagged {ref.defaults.cron_tag} your user crontab

The job also runs when it’s loaded, which on macOS includes every login, and on systemd one minute after boot. Neither launchd nor systemd wakes a sleeping machine for it; syncing resumes when the machine wakes.

Terminal window
dotsync agent status # installed? loaded? running?
dotsync agent install # install, or re-install after a change
dotsync agent uninstall
~/Library/LaunchAgents/io.github.dotsync.plist (abridged)
<key>ProgramArguments</key>
<array>
<string>/Users/you/.local/bin/dotsync</string>
<string>sync</string>
<string>-q</string>
</array>
<key>StartInterval</key><integer>300</integer>
<key>RunAtLoad</key><true/>
<key>ProcessType</key><string>Background</string>
<key>LowPriorityIO</key><true/>
<key>LowPriorityBackgroundIO</key><true/>
<key>Nice</key><integer>10</integer>
<key>StandardOutPath</key><string>/Users/you/.local/state/dotsync/agent.log</string>
<key>StandardErrorPath</key><string>/Users/you/.local/state/dotsync/agent.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key><string>/opt/homebrew/bin:/usr/bin:/bin:…</string>
<key>HOME</key><string>/Users/you</string>
</dict>

The job runs ~/.local/bin/dotsync, the copy init installs, or the binary you ran agent install with if that copy doesn’t exist.

launchd and cron don’t read your shell’s configuration, so dotsync writes the environment into the job itself when it installs it:

  • PATH: the directories of the PATH you ran dotsync from, followed by /usr/bin and /bin, so the job finds the same git, ssh and credential helpers as your shell
  • HOME
  • XDG_CONFIG_HOME, XDG_DATA_HOME, XDG_STATE_HOME and XDG_CACHE_HOME, if you’ve set them

If you move git, or change any of these, run dotsync agent install again.

dotsync update and the install script re-install the agent after replacing the binary, so its schedule, priority and environment always match the version you run. dotsync doctor warns when the installed definition was written by a different version (dotsync agent install fixes it). A different PATH alone doesn’t count.

Set interval (in seconds) in ~/.config/dotsync/config.json, then run dotsync agent install. Or pass --interval when you first run init.

The agent is built to go unnoticed on a laptop battery, and to be predictable.

When nothing changed, which is almost every run, a sync:

  • runs 2 git processes: a fetch and a rev-parse
  • writes nothing except the state file’s modification time, which records when the last sync ran: no log line, no git index, reflog or FETCH_HEAD, no state rewrite
  • reads no file contents: a file whose size, modification time, change time, inode and mode are unchanged since dotsync last hashed it isn’t read again. As in git, a file modified in the last two seconds is always re-read.
  • takes around 65 ms, most of it the network round trip of the fetch, and peaks under 10 MiB of memory

A machine with a conflict or a blocked file logs that line on every sync, until you deal with it.

Priority. On macOS the job runs as a launchd Background process with low-priority I/O and Nice 10. On systemd it uses the idle CPU and I/O scheduling classes and Nice=10. The Go runtime runs on a single CPU (GOMAXPROCS=1) with a 64 MiB soft memory limit.

No randomness.

  • Syncs run on a fixed interval, with no random delay.
  • The systemd timer has RandomizedDelaySec=0 and a fixed one-minute accuracy window, so systemd can batch the wake-up with others.
  • When another machine pushes first, dotsync redoes its pass straight away, with no random back-off, up to 5 times.
  • Housekeeping runs on a fixed calendar, not whenever git decides: backups are pruned at most once a day, the cache clone is fully reset at least once a day, and git gc runs once a week. git’s own automatic gc and maintenance are turned off in the cache clone.

The agent can’t type passwords or passphrases, so git ls-remote <your-url> must succeed without any prompt. dotsync runs git with GIT_TERMINAL_PROMPT=0, and ssh with BatchMode=yes and a 15-second connection timeout (unless you set GIT_SSH_COMMAND yourself), so a missing credential fails fast instead of hanging.

Store the key’s passphrase in the Keychain once, and let ssh load it automatically:

~/.ssh/config
Host github.com
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
Terminal window
ssh-add --apple-use-keychain ~/.ssh/id_ed25519

Check with dotsync doctor, which reaches the remote exactly as the agent does. When git fails in a way you need to fix, such as a rejected key, missing credentials or an unknown host key, doctor, status and the agent’s notification all name the problem and the fix.

Where What
~/.local/state/dotsync/sync.log every change, conflict and error, and one line for each sync that did something or whose outcome changed. Rotated to sync.log.1 at 1 MiB
~/.local/state/dotsync/agent.log anything the job printed, with launchd and cron. dotsync prints nothing with -q unless a sync fails outright
journalctl --user -u dotsync the same, with systemd

The agent posts a desktop notification when a new conflict appears, and when git starts failing in a way you need to fix. It notifies once, not on every run. To turn notifications off on a machine, set "notifications": false in its configuration; the next sync respects it. The DOTSYNC_NO_NOTIFY environment variable also turns them off, for runs that inherit it.

  • Linux: through notify-send, with the dotsync icon.
  • macOS: through Notification Center, with the Script Editor icon. Install terminal-notifier (brew install terminal-notifier) and dotsync uses it to show its own icon.