Keep secrets out
dotsync’s rule is simple: secrets never leave the machine. They aren’t encrypted into the repository; they’re kept out of it. Before any file is uploaded, dotsync checks its path and every line of its content against a set of rules for private keys, cloud credentials and tokens.
What you’ll see
Section titled “What you’ll see”Adding a file that looks like a credential is refused:
dotsync add ~/.aws/credentials
dotsync: /Users/you/.aws/credentials: refusing to manage it: path looks like a secret (matches '.aws/credentials') (use -allow-secrets if the remote is meant to hold it)A managed file that gains a secret, say after you paste a token into your shell configuration, becomes BLOCKED. Its new version isn’t sent, the version already in the repository stays as it was, and sync and status tell you why:
dotsync sync
BLOCKED ~/.config/fish/config.fish (content looks like a GitHub token)
dotsync: 1 blockeddotsync status
…
SOURCE TARGET STATUS DESCRIPTION
fish/config.fish ~/.config/fish/config.fish BLOCKED Fish shell configuration
↳ content looks like a GitHub token
gitconfig ~/.gitconfig ok Git identity and aliases
BLOCKED files look like they contain secrets, so their changes stay on this machine.
remove the secret, or mark a false positive with a `dotsync:allow-secret` comment on that line:
https://pungoyal.github.io/dotsync/guides/secrets/Remove the secret and the file syncs again on the next run. dotsync doctor also counts blocked files.
Inside a managed directory, credential files such as ~/.config/gh/hosts.yml are held back the same way, while the rest of the directory syncs. add warns about them, and status shows them as BLOCKED until you ignore them.
Move secrets out of synced files
Section titled “Move secrets out of synced files”The clean fix is to keep each secret in a separate, unmanaged file, and have the synced file load it:
test -f ~/.config/fish/secrets.fish; and source ~/.config/fish/secrets.fishset -gx GITHUB_TOKEN ghp_…[ -f ~/.secrets.sh ] && . ~/.secrets.shexport GITHUB_TOKEN=ghp_…[include] path = ~/.gitconfig.local[github] token = ghp_…If the synced file is inside a managed directory, as secrets.fish would be when all of ~/.config/fish is managed, also ignore it: dotsync set ~/.config/fish --ignore secrets.fish. Its name would keep it from being uploaded anyway, but it would show up as BLOCKED on every sync.
Better still, fetch secrets when they’re needed, from a password manager or the OS keychain: op read, pass, security find-generic-password on macOS, secret-tool on Linux. A value read from a variable or a command isn’t a secret as far as the rules are concerned:
export GITHUB_TOKEN=$(op read op://dev/github/token) # fine: read at runtimeexport GITHUB_TOKEN=ghp_… # blockedEncrypted files
Section titled “Encrypted files”Files encrypted with age or sops sync like any other file, even when their name looks like a secret, as with .env.json or secrets.yaml. Only ciphertext reaches the repository. The age private key that decrypts them is always refused, so copy it to each new machine yourself. Encrypted files lists exactly what is checked, and Use with mise shows a complete setup.
False positives
Section titled “False positives”Sometimes a line only looks like a secret. You have two ways out:
-
One line: put
dotsync:allow-secretanywhere on it, usually in a comment. Here the value names a keychain item rather than holding a key, but it looks like an assignment of one:api_key = keychain:openai # dotsync:allow-secret -
A whole entry: add it with
dotsync add --allow-secrets, or turn the rules off for an existing entry withdotsync set <entry> --allow-secrets(and back on with--allow-secrets=false).
Limits
Section titled “Limits”The rules match patterns: a secret in a format they don’t know gets through. They reduce the risk of a mistake; they don’t make the repository a safe place for credentials. Keep the repository private, and treat everything in it as readable by anyone who can read the repository. See the security model.