Push / pull / versions
The day-to-day rhythm.
vsync push <env>
[1/5] zipping infra/vault/dev/
[2/5] sealing manifest ts=20260516-113304
[3/5] encrypting
[4/5] uploading 646 bytes → s3://muthuishere-vsync-e2e/vsync/dev/versions/20260516-113304.enc
[5/5] updating pointer → s3://muthuishere-vsync-e2e/vsync/dev/latest
✅ pushed vsync/dev (version: 20260516-113304)Five steps:
- Zip the resolved vault folder (
infra/vault/<env>/by default). - Manifest-seal — embeds the timestamp inside the encrypted plaintext (anti-rollback; see Crypto envelopes).
- Encrypt with AES-256-GCM using your per-(repo, env) AES key.
- Upload to
s3://<bucket>/<repo>/<env>/versions/<ts>.enc. - Update pointer — atomically replaces
s3://<bucket>/<repo>/<env>/latestwith the new version's timestamp.
If anyone's pulling concurrently, they'll see either the old version or the new — never a half-written one.
vsync pull <env>
[1/6] backing up local infra/vault/dev/ (if any)
→ /Users/muthu/.config/vsync/backups/dev-20260516-113414.zip.enc
[2/6] reading pointer s3://…/vsync/dev/latest
[3/6] downloading version 20260516-113343 (vsync/dev/versions/20260516-113343.enc)
[4/6] decrypting
[5/6] verifying manifest ts
[6/6] unzipping into /Users/muthu/projects/myrepo
✅ pulled vsync/dev version 20260516-113343Six steps:
- Backup the current vault folder (encrypted with the same key) to
~/.config/vsync/backups/. Two-deep rolling buffer — older backups auto-pruned. See Recovering a local backup below. - Read pointer — fetches the
latestobject to learn which version to pull. - Download the version's
.encblob. - Decrypt with the keychain key + PBKDF2 over the per-repo salt.
- Verify manifest ts — refuses to install a bundle whose embedded timestamp doesn't match the pointer. This rejects "pointer flipped to old version" attacks from anyone with bucket-write but not the key.
- Unzip into the resolved vault folder. Existing files are overwritten.
vsync versions <env>
s3://muthuishere-vsync-e2e/vsync/regression/versions/ (5 versions)
* 20260516-180858 646 B 2h ago
20260516-174208 644 B 5h ago
20260515-201410 640 B 1d ago
20260515-180858 646 B 1d ago
20260515-152233 640 B 2d agoRead-only list of every version on S3 for this (repo, env), newest-first. The * marker shows which version latest currently points at. No decrypt needed; only the disk config (S3 creds) is read.
Useful for:
- Verifying your
pushlanded. - Confirming a teammate's recent push before you
pull. - Spotting unexpected versions (someone else's machine pushed?).
Switching envs mid-day
bash
vsync use production # repoint ./.env → infra/vault/production/.env.production
bun run dev # restart with prod creds (be careful!)
vsync use dev # back to devSee Switching envs.
Backups & recovery
Before each pull, vsync writes the existing vault folder to ~/.config/vsync/backups/<env>-<ts>.zip.enc (two-deep rolling buffer). The format is AES-256-GCM with the same per-(repo, env) keychain key + salt.
To decrypt one by hand (rare — pull itself is the recovery path 99% of the time):
- Get the key:
- macOS:
security find-generic-password -s tools.vsync -a <repo>/<env> -w - Linux:
secret-tool lookup service tools.vsync account <repo>/<env> - Windows: Credential Manager UI under "tools.vsync"
- macOS:
- Get the salt:
gunzip -c ~/.config/vsync/<repo>/env_<env> | jq -r .encryption.salt - Envelope is
RQE1(4-byte magic) + 12-byte IV + AES-GCM ciphertext. Derive:AES-GCM key = PBKDF2-SHA256(keychain-key, salt, 600k iters).
In practice, don't lose the keychain entry, and you'll never need this.