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.
| Platform | Settings (sites.json) | State and logs |
|---|---|---|
| Windows | %APPDATA%\sync-servers | %LOCALAPPDATA%\sync-servers, logs in its logs folder |
| macOS | ~/Library/Application Support/sync-servers | its 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).
| Path | Holds |
|---|---|
sites/<site id>/<server id>.json | The sync records: both sides of every file as they were at its last synchronization. |
sites/<site id>/hashes.json | The 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-*.jsonl | One 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
| Field | Default | Meaning |
|---|---|---|
id | generated | A random UUID that stays the same when the site is renamed. |
name | The name you know the site by; unique, ignoring case. | |
localRoot | The local site folder, an absolute path. | |
servers | [] | The site's servers, described below. |
remoteServerId | the first server | The server synchronization uses. |
testingServerId | none | The server automatic pushes go to, such as the extension's upload on save. |
local.webUrl | none | The local web address, such as http://localhost:8080/. |
local.caseSensitive | probed | Whether local names differ by case. Unset: the file system is asked. |
local.cache | true | Keep file hashes between runs in checksum mode. |
cloaking.enabled | true | Turns cloaking on or off, keeping the patterns. Off also turns off the global patterns for the site. |
cloaking.useGlobalPatterns | true | Use 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.maintainInfo | true | Keep sync records, so a change on both sides is detected. |
sync.compare | metadata | metadata compares size and time; checksum also compares SHA-256 hashes where both are known. |
sync.mtimeToleranceSeconds | 2 | Times this close count as equal, for servers that round them. |
sync.symlinks | skip | Which symbolic links in the local folder are followed: none (skip), those inside the site (follow-inside-root) or all (follow); see Symbolic links. |
sync.concurrency | 4 | Transfers at once, from 1 to 16. |
sync.guard.enabled | true | Hold back sensitive files; see The sensitive-file guard. |
sync.guard.patterns | [] | More patterns to hold back, added to the built-in list. |
sync.massDelete.maxCount | 50 | Deleting more files than this asks you to type the count. |
sync.massDelete.maxPercent | 25 | Deleting more than this share of a side's files asks too. |
sync.recovery.enabled | true | Keep local files that a run deletes or overwrites. |
sync.recovery.keepDays | 14 | How long they are kept. |
sync.recovery.maxMegabytes | 1024 | The most the recovery folder of a site may hold; the oldest runs go first. |
Server fields
| Field | Default | Meaning |
|---|---|---|
id | generated | A random UUID that stays the same when the server is renamed. |
name | Unique within the site, ignoring case. | |
protocol | sftp, ftps, ftps-implicit, ftp or local. | |
host | The 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. | |
port | 22, 21 or 990 | 22 for SFTP, 21 for FTP and explicit FTPS, 990 for implicit FTPS. |
username | The account to sign in as. | |
auth.method | password | password, or for SFTP key or agent. |
auth.keyPath | The private key file of the key method; read when connecting, never copied. | |
auth.savePassword | false | Keep the password or key passphrase in the credential store. |
remoteRoot | The 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. | |
webUrl | none | The address of the site on this server. |
options.timeoutSeconds | 30 | How long a connection or an operation may wait. |
options.ipFamily | either | 4 or 6 to use one IP version only. |
options.passive | true | FTP passive mode; active mode is not supported. |
options.allowInsecure | Required for plain ftp, which sends the password in clear text. | |
options.allowRootDeletes | Allow deletes when the root directory is / or a drive root. | |
options.legacyAlgorithms | SFTP: also offer older algorithms for an old server, with a warning on every connection. | |
options.tls.caFile | FTPS: a PEM file of extra certificate authorities, for a private CA. | |
options.tls.servername | FTPS: the name to verify the certificate against, when it differs from host. | |
options.tls.minVersion | TLSv1.2 | FTPS: the lowest TLS version accepted. |
options.tls.trustedCertificate | FTPS: 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.hostKey | SFTP: the pinned host key, set when you trust it on first connection. | |
options.uploadOnSave | false | Used by the VS Code extension: put a file when it is saved. |
options.remoteFileMode | server default | An octal mode such as 644 for uploaded files. |
options.remoteDirMode | server default | An 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:
| Pattern | Cloaks |
|---|---|
.psd | Every 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.psd | Uncloaks 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.
Symbolic links
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.
| Value | Follows |
|---|---|
skip (default) | No link. Each one is listed as a problem, and nothing is read or written through it. |
follow-inside-root | Links that lead to a place inside the site's folder. |
follow | Every 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:
- A file link is a file at the link's path. A Put sends the file it points to; a Get writes that file and keeps the link.
- A folder link is a folder whose files are listed under the link's path. A file inside it is a file of the folder the link points to, so a Get or a delete changes it there.
- A local delete of a path that is itself a link removes the link, never what it points to, and the recovery folder keeps a copy of the content.
- A folder that is a link is never removed when a delete leaves it empty.
- Two names for one file, such as a link and the file it points to, are two paths of the site. Both are put, and a Get through one changes the other, which the next sync puts too. Rows on one file run one at a time.
- Cloaking and the guard see through links: a link to a cloaked file or folder is cloaked, and a link to a file such as
.envis held back like the file.
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:
- The operating system's credential store, when the server has
savePasswordon: Windows Credential Manager, the macOS Keychain, or the Secret Service keyring on Linux. Entries use the servicesync-servers. - The environment:
SYNC_SERVERS_PASSWORD, orSYNC_SERVERS_KEY_PASSPHRASEfor a key. - 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
| Variable | Does |
|---|---|
SYNC_SERVERS_HOME | Moves the settings and state folders. |
SYNC_SERVERS_SITE | The site to use when a command names none. |
SYNC_SERVERS_PASSWORD | The password of the server in use, for unattended runs. |
SYNC_SERVERS_KEY_PASSPHRASE | The private key passphrase of the server in use. |
SYNC_SERVERS_KEYRING | off turns the operating system's credential store off. |
SYNC_SERVERS_UI | How previews are shown: tui, editor, plain or none. |
SYNC_SERVERS_EDITOR, VISUAL, EDITOR | The editor for editor mode and --editor, the first one set. Otherwise Notepad on Windows, then nano or vi. |
SSH_AUTH_SOCK | The SSH agent for the agent sign-in method. On Windows the OpenSSH agent is used when it is unset. |
NO_COLOR | Turns colour off; nothing depends on colour. |
TERM=dumb | Uses 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.