Comparing a platform with Varlatch: varlatch sync check
varlatch sync check answers one question: does a platform still hold the
values the Environment holds? It reads the platform’s current values,
compares them in memory with the Environment’s, and prints one status per
key. It never prints a value, a hash, a length, or anything else derived
from one, so it works in assisted mode too, including
when the Environment holds a Secret too short to mask.
Use it instead of printing the environment through varlatch run and
comparing it with the platform. In assisted mode that output is masked, so
every Secret looks different, and outside it every value ends up in a log.
varlatch sync check --platform coolify --base https://coolify.example.com --app <uuid> -e production --token-env COOLIFY_TOKENvarlatch sync check --platform convex --base https://happy-animal-123.convex.cloud -e production --token-env CONVEX_DEPLOY_KEY --jsonThe options are those of varlatch sync push, without --build-time or --platform-expands:
--platform, --base, the destination (--app <uuid>, or
--service <uuid> for a service, for Coolify;
--repo <name> and --gh-environment <name> for GitHub Actions; nothing
for Convex, whose --base is the deployment URL), -e <env>,
--server <url>, --token-env VAR (the variable holding the platform’s
credential, VARLATCH_SYNC_TOKEN by default), --map NAME[=DEST],
--exclude NAME|PREFIX*, and --json. Options are checked strictly: an
unknown or misspelled option exits with status 64 and checks nothing.
Which keys
Section titled “Which keys”- Every item of the active Contract, less
--exclude. A key on the platform that is not in the Contract (Coolify’s ownCOOLIFY_FQDN, for example) is never compared or reported. - With
--map NAME[=DEST], exactly the mapped items, each compared with the platform keyDEST(default: the same name). A name that is neither stored in the Environment nor in its Contract exits with status 64: a misspelled name must not pass as “absent”. - Without an active Contract (or without
contract.read), the stored items, and the command says so;extra-on-platformis then not reported. - Coolify keeps a preview-deployment copy of every variable. Only the
production rows are compared, as
sync pushwrites only those. - For a Coolify service, the names Coolify sets itself (
SERVICE_FQDN_*,SERVICE_URL_*,SERVICE_NAME_*,COOLIFY_*) areunreadablewithinvalid-name: a Sync Target never manages them.
Statuses
Section titled “Statuses”| Status | Meaning |
|---|---|
match |
Both hold the key, with the same value, byte for byte |
differs |
Both hold the key, with different values (whitespace counts) |
missing-on-platform |
Stored in Varlatch, not on the platform |
extra-on-platform |
On the platform, a Contract key Varlatch does not store |
unreadable |
Not compared: the platform’s values are write-only (platform-write-only, GitHub Actions secrets), the platform holds the key but withholds its value from this credential (platform-withheld: a Coolify token without Read sensitive, or a variable shown once), this identity may not read the value in Varlatch (withheld), or the platform cannot store the name or sets it itself (invalid-name) |
absent |
Neither holds it; not drift |
Exit status
Section titled “Exit status”- 0: every compared key matches.
- 1: drift: at least one key differs, is missing on the platform, or is extra on it. Drift wins over keys that could not be compared.
- 2: not everything could be checked, and no drift was found: the
platform could not be read (no credential in the
--token-envvariable, a refused credential, an unreadable response), or some key isunreadable. - Varlatch’s own failures keep their usual statuses: 64 for a wrong command line, 69 when the Varlatch server cannot be reached, 77 when you are not signed in or the request is denied. See scripting.
--json prints one document: result (match, drift, incomplete, or
unchecked), the platform, base, destination, and environment, keySet
(contract, stored, or map), counts per status, keys (each with
name, destination, status, and for unreadable a reason),
usage, and exitCode. It never holds a value.
For a Coolify service whose compose file the token can read, usage lists
how the compose file uses the compared names, as the integration card
does: owned (Coolify sets the name itself), shadowed (a container’s
compose settings give it another value, which wins; with containers),
not-delivered (no container uses it), unknown-form (a form Varlatch
does not read), generated-once (a Coolify-generated password or user),
or one usage-unchecked entry with a reason. The human form prints the
same as usage: lines. Names and container names only.
What it reads and discloses
Section titled “What it reads and discloses”- The Environment’s metadata and non-sensitive values, and the active Contract. No Secret yet.
- The platform’s values, with the credential from
--token-env, through the same adaptersync pushuses (https only, no redirects followed, the host pinned). If the platform cannot be read, the command stops here: nothing is disclosed. A failure is reported by its status or error class, never by the platform’s response, which may quote values. - Only the Secrets the platform also holds and lets the credential read,
by name, in one requested disclosure. It needs
secret.revealand is audited like every disclosure. A Secret that is missing on the platform, or whose value the platform withholds, is reported without its value being read.
The values stay in the command’s memory and are compared exactly; nothing is written anywhere.
Limits
Section titled “Limits”- The platform credential can read the platform’s values, so whoever holds it can already see what the platform holds. The check adds one fact per key: whether Varlatch’s value is the same.
- It is an equality test. Someone who can write the platform could test guesses by setting a value and running the check, one audited disclosure per guess. That is deliberate misuse, outside what assisted mode protects against.
- GitHub Actions secrets cannot be read back, so they are never compared
(status
unreadable, exit 2).

