Configuration

One list of sites, shared by the command line and the VS Code extension, plus per-machine state that never leaves the computer.

Where things are kept

Settings roam with your user profile; state stays on the machine. sync-servers config path prints the folders in use.

Folders per platform
PlatformSettings (sites.json)State and logs
Windows%APPDATA%\sync-servers%LOCALAPPDATA%\sync-servers, logs in its logs folder
macOS~/Library/Application Support/sync-serversits state folder; logs in ~/Library/Logs/sync-servers
Linux and other systems$XDG_CONFIG_HOME/sync-servers, else ~/.config/sync-servers$XDG_STATE_HOME/sync-servers, else ~/.local/state/sync-servers

SYNC_SERVERS_HOME moves both: settings to that folder and state to its state subfolder. --config-dir and --state-dir move one for a single command. Folders are created private to you (0700 on Linux and macOS).

What the state folder holds
PathHolds
sites/<site id>/<server id>.jsonThe sync records: both sides of every file as they were at its last synchronization.
sites/<site id>/hashes.jsonThe scan cache of file hashes, used in checksum mode.
recovery/<site id>/<run>/Local files a run deleted or overwrote, kept 14 days by default.
logs/run-*.jsonlOne log per run, the newest 30 kept, with every secret removed.

The list of sites

sites.json is plain JSON, described by a JSON Schema that editors use for completion. Every write is atomic, keeps the previous version as sites.json.bak, and takes a lock file so two programs never lose each other's changes. A file that does not validate is never rewritten. Edit it with the commands, the terminal interface, sync-servers site edit --editor for one site, or sync-servers config edit for the whole file. Both reopen your editor with the problems listed until the JSON is valid, and config edit works even when the file does not load.

{
  "$schema": "https://isocialpractice.github.io/cmd-sync-servers/schema/site.schema.json",
  "schemaVersion": 1,
  "cloaking": { "patterns": [".git/", "node_modules/"] },
  "sites": [
    {
      "id": "3f2b7c1e-8a4d-4f6b-9c2e-1d5a7b9e0c31",
      "name": "My Site",
      "localRoot": "/home/<user>/sites/my-site",
      "servers": [
        {
          "id": "9a1c3e5f-7b2d-4e6a-8c0f-2b4d6e8a0c13",
          "name": "Production",
          "protocol": "sftp",
          "host": "sftp.example.com",
          "username": "demo-user",
          "auth": { "method": "password", "savePassword": true },
          "remoteRoot": "/var/www/my-site",
          "webUrl": "https://www.example.com/"
        }
      ],
      "remoteServerId": "9a1c3e5f-7b2d-4e6a-8c0f-2b4d6e8a0c13",
      "cloaking": { "patterns": [".psd"] }
    }
  ]
}

Fields left out take their defaults. Passwords never appear in this file. At the top, cloaking.patterns holds the global cloaking patterns, which every site uses before its own; it is written only while it holds a pattern.

Site fields

Fields of a site
FieldDefaultMeaning
idgeneratedA random UUID that stays the same when the site is renamed.
nameThe name you know the site by; unique, ignoring case.
localRootThe local site folder, an absolute path.
servers[]The site's servers, described below.
remoteServerIdthe first serverThe server synchronization uses.
testingServerIdnoneThe server automatic pushes go to, such as the extension's upload on save.
local.webUrlnoneThe local web address, such as http://localhost:8080/.
local.caseSensitiveprobedWhether local names differ by case. Unset: the file system is asked.
local.cachetrueKeep file hashes between runs in checksum mode.
cloaking.enabledtrueTurns cloaking on or off, keeping the patterns. Off also turns off the global patterns for the site.
cloaking.useGlobalPatternstrueUse the global patterns before the site's own. Written only when false.
cloaking.patterns[]Cloaking patterns; see Cloaking.
cloaking.paths[]Site-relative files and folders cloaked one by one.
sync.maintainInfotrueKeep sync records, so a change on both sides is detected.
sync.comparemetadatametadata compares size and time; checksum also compares SHA-256 hashes where both are known.
sync.mtimeToleranceSeconds2Times this close count as equal, for servers that round them.
sync.symlinksskipWhich symbolic links in the local folder are followed: none (skip), those inside the site (follow-inside-root) or all (follow); see Symbolic links.
sync.concurrency4Transfers at once, from 1 to 16.
sync.guard.enabledtrueHold back sensitive files; see The sensitive-file guard.
sync.guard.patterns[]More patterns to hold back, added to the built-in list.
sync.massDelete.maxCount50Deleting more files than this asks you to type the count.
sync.massDelete.maxPercent25Deleting more than this share of a side's files asks too.
sync.recovery.enabledtrueKeep local files that a run deletes or overwrites.
sync.recovery.keepDays14How long they are kept.
sync.recovery.maxMegabytes1024The most the recovery folder of a site may hold; the oldest runs go first.

Server fields

Fields of a server
FieldDefaultMeaning
idgeneratedA random UUID that stays the same when the server is renamed.
nameUnique within the site, ignoring case.
protocolsftp, ftps, ftps-implicit, ftp or local.
hostThe host name or IP address alone, such as sftp.example.com or 203.0.113.10. A port, a user or a protocol in it, such as server1.hosting.example.net:2083, is refused with the field to use instead. Not used by local.
port22, 21 or 99022 for SFTP, 21 for FTP and explicit FTPS, 990 for implicit FTPS.
usernameThe account to sign in as.
auth.methodpasswordpassword, or for SFTP key or agent.
auth.keyPathThe private key file of the key method; read when connecting, never copied.
auth.savePasswordfalseKeep the password or key passphrase in the credential store.
remoteRootThe site folder on the server, starting with /, as the account sees it; for local, an absolute or network path. Many hosting accounts open in their own folder and see it as /, so a path from a control panel, such as /home/<user>/example.com/my-site, does not exist for them. sync-servers server test shows the folder the account opens in, and a root that is not found names it and what it holds.
webUrlnoneThe address of the site on this server.
options.timeoutSeconds30How long a connection or an operation may wait.
options.ipFamilyeither4 or 6 to use one IP version only.
options.passivetrueFTP passive mode; active mode is not supported.
options.allowInsecureRequired for plain ftp, which sends the password in clear text.
options.allowRootDeletesAllow deletes when the root directory is / or a drive root.
options.legacyAlgorithmsSFTP: also offer older algorithms for an old server, with a warning on every connection.
options.tls.caFileFTPS: a PEM file of extra certificate authorities, for a private CA.
options.tls.servernameFTPS: the name to verify the certificate against, when it differs from host.
options.tls.minVersionTLSv1.2FTPS: the lowest TLS version accepted.
options.tls.trustedCertificateFTPS: a certificate you chose to trust after it failed verification: sha256 (colon-separated hex), validTo and pinnedAt. Normal verification runs first; an expired certificate is never trusted. Set when you trust a certificate in a terminal; removed with --forget-certificate. See Certificates.
options.hostKeySFTP: the pinned host key, set when you trust it on first connection.
options.uploadOnSavefalseUsed by the VS Code extension: put a file when it is saved.
options.remoteFileModeserver defaultAn octal mode such as 644 for uploaded files.
options.remoteDirModeserver defaultAn octal mode such as 755 for new folders.

Cloaking

Cloaked files and folders are never scanned, listed, transferred or deleted, on either side. Patterns use the rules of .gitignore files, with one addition for suffixes:

Cloaking patterns
PatternCloaks
.psdEvery name ending in .psd: a token that starts with a dot and has no * or / is a suffix.
archive*Every file or folder whose name starts with archive, at any depth.
node_modules/Every folder named node_modules, and everything in it.
/drafts/Only the drafts folder at the top of the site.
!keep.psdUncloaks what an earlier pattern cloaked.

Names are matched ignoring case on Windows and macOS. Single paths can be cloaked too, and cloak everything beneath them:

sync-servers cloak .psd "archive*" node_modules/    # patterns
sync-servers cloak img/raw                          # an existing path in the site
sync-servers cloak --list
sync-servers uncloak .psd

Global patterns

Files that belong to no site, such as version control folders, installed packages and the files an operating system leaves behind, can be cloaked once for every site. Every site uses the global patterns before its own, so a site's !pattern re-includes what a global pattern cloaks, and a pattern that starts with / is anchored at each site's local root. Turning cloaking off for a site turns the global patterns off there too.

sync-servers cloak --global .git/ .github/ node_modules/ .DS_Store Thumbs.db desktop.ini "*.lnk"
sync-servers cloak --global                              # list them
sync-servers uncloak --global .github/
sync-servers cloak --site "My Site" --ignore-global      # one site stops using them
sync-servers cloak --site "My Site" --use-global         # and uses them again

No global pattern is built in. The list above is a starting point for a typical web site; keep only what your sites never publish. In the terminal interface, c on the Sites screen edits the global patterns, and Site Setup's Cloaking category shows them beside a Use global patterns switch.

The sensitive-file guard

Some files are almost never meant to be published. A file matching one of these patterns is listed in the preview unchecked with the pattern it matched, and moves only when you check it. It is never hidden.

.env  .env.*  *.pem  *.key  *.p12  *.pfx  *.kdbx  id_rsa*  id_ecdsa*  id_ed25519*
.git/  .hg/  .svn/  .ssh/  .aws/  .npmrc  .pypirc  .netrc  *.sqlite-journal

Add patterns of your own in sync.guard.patterns or the Security category of Site Setup. Unattended runs with --yes never transfer a guarded file.

A site's local folder can hold symbolic links and, on Windows, junctions. sync.symlinks says which of them the site follows. Set it with sync-servers site edit --symlinks, in the Synchronization category of Site Setup, or with sync-servers config edit.

Values of sync.symlinks
ValueFollows
skip (default)No link. Each one is listed as a problem, and nothing is read or written through it.
follow-inside-rootLinks that lead to a place inside the site's folder.
followEvery link, including links to files and folders outside the site's folder.

A followed link is synchronized as what it points to, so the server gets ordinary files and folders, never a link:

Never followed: links on the server, links to something that does not exist, links back into a folder above them, and, with follow-inside-root, links that lead out of the site. Each is a problem row that says why; see Link problems.

With follow, a Get or a delete through a link that leads out of the site changes a file outside the site's folder, and the preview says so on each such row. A link to a large folder makes every scan read all of it, so cloak what the site does not need. A local root that is itself a link is followed with every value.

On Windows, junctions and symbolic links both count. Creating a junction needs no privilege; creating a file symbolic link needs Developer Mode or an administrator. Shortcuts (.lnk files) are ordinary files, not links. In cmd.exe, from the site's folder:

mklink /J fonts C:\Users\<user>\Shared\fonts
mklink css\theme.css C:\Users\<user>\Shared\theme\theme.css

On macOS and Linux:

ln -s ~/shared/fonts fonts
ln -s ~/shared/theme/theme.css css/theme.css

Secrets

Passwords and key passphrases are never written to a file. When a connection needs one, it is looked for in this order:

  1. The operating system's credential store, when the server has savePassword on: Windows Credential Manager, the macOS Keychain, or the Secret Service keyring on Linux. Entries use the service sync-servers.
  2. The environment: SYNC_SERVERS_PASSWORD, or SYNC_SERVERS_KEY_PASSPHRASE for a key.
  3. A question in the terminal, typed without being shown. With nobody to ask, the run fails with E_AUTH.
sync-servers password set Production --site "My Site"     # save it
printf %s "$SECRET" | sync-servers password set Production --stdin
sync-servers password forget Production                    # remove it

A secret is asked for only after the server's host key or TLS certificate has been verified, and it is removed from every log and error message. A saved secret is tied to the protocol, address, port and user it was saved for; after any of them changes, it is no longer sent and you are asked again, so save it again with password set. SYNC_SERVERS_KEYRING=off turns the credential store off.

Environment variables

Variables sync-servers reads
VariableDoes
SYNC_SERVERS_HOMEMoves the settings and state folders.
SYNC_SERVERS_SITEThe site to use when a command names none.
SYNC_SERVERS_PASSWORDThe password of the server in use, for unattended runs.
SYNC_SERVERS_KEY_PASSPHRASEThe private key passphrase of the server in use.
SYNC_SERVERS_KEYRINGoff turns the operating system's credential store off.
SYNC_SERVERS_UIHow previews are shown: tui, editor, plain or none.
SYNC_SERVERS_EDITOR, VISUAL, EDITORThe editor for editor mode and --editor, the first one set. Otherwise Notepad on Windows, then nano or vi.
SSH_AUTH_SOCKThe SSH agent for the agent sign-in method. On Windows the OpenSSH agent is used when it is unset.
NO_COLORTurns colour off; nothing depends on colour.
TERM=dumbUses ASCII symbols and plain prompts.

Which site a command means

A command acts on the site named by its argument or --site, then SYNC_SERVERS_SITE, then the site whose local folder holds the current folder (the deepest one when sites are nested). In a terminal, you are otherwise asked to choose; in a script, the command stops with a usage error that lists the sites.