ConfigSync Internals: How Content-Hash Caching Works
Second push taking under a second? That is content-hash caching at work. Here is a deep dive into how ConfigSync skips redundant encryption using SHA-256 hashing and mtime checks.
The Cost of Encryption
Every file ConfigSync tracks needs to be encrypted before it leaves your machine. The encryption pipeline involves reading the file, deriving a per-file key via PBKDF2, wrapping the content with Fernet (AES-128-CBC), and base64 encoding the result. For a single file, this takes milliseconds. For hundreds of files, it adds up.
But here is the thing: most pushes involve zero actual changes. You are pushing to ensure the server has your latest state, not because you modified 300 files since the last push. ConfigSync needs a way to detect "nothing changed" and skip the expensive encryption step entirely.
The Hash Store
ConfigSync maintains a local cache at ~/.configsync/state/hashes.json. For each tracked file, it stores the SHA-256 content hash, the file's last modification time (mtime), the file size, and a reference to the cached encrypted output:
This file is the backbone of the caching system. It lets ConfigSync answer "has this file changed?" without reading the file contents or performing any encryption.
The Two-Level Change Detection
On every push, each tracked file goes through a two-level check:
Level 1 is a single stat() system call — nearly free. It catches the overwhelming majority of cases. Level 2 handles edge cases like git checkouts that update mtime without changing content. Level 3 does the actual work, but only for files that genuinely changed.
git checkout, a touch command, or some editors that rewrite files on save can update mtime even when the content is identical. The content hash catches these false positives.The Fast Path in Numbers
On a typical machine tracking 200 files where nothing has changed:
Two hundred stat() calls complete in 60 milliseconds. Compare that to reading, hashing, and encrypting 200 files, which would take several seconds. The caching makes the no-change case essentially free.
When a File Actually Changes
When you edit a file and push, only that file goes through the full pipeline:
199 files skip via the fast path. One file gets read, hashed, encrypted, and uploaded. The total operation takes 300 milliseconds instead of the seconds it would take to process all 200 files.
The --changed Extension
The --changed flag extends caching to the upload step. Normally, ConfigSync uploads a complete state snapshot, reusing cached encrypted content for unchanged files. With --changed, it sends only the delta — files that actually changed since the last push.
Watch mode uses this automatically. When you save a file and watch mode triggers a push, it sends only the changed file. A few kilobytes instead of the full multi- megabyte state snapshot.
Design Inspiration
This approach mirrors how Turborepo uses content hashing to skip redundant builds. Turborepo hashes source files and skips rebuilding if inputs have not changed. ConfigSync hashes config files and skips re-encrypting if content has not changed. The principle is the same: the fastest work is the work you never do.
Ready to try ConfigSync?
Sync your entire dev environment across machines in minutes. Free forever for up to 3 devices.