punix verify¶
Report config-file and store drift against a stack's current generation.
What it answers¶
Is this host still what I declared?: the question Chef and Puppet earn their agents with, and the one Ansible cannot answer at all because it keeps no record of the declaration.
Punix already records a sha256 for every config file in every generation, and the store is content-addressed. So this is a read-only command over data the deploy already wrote: no agent, no daemon, no convergence loop, nothing installed on the target beyond what a deploy already needed.
It never repairs. A drifted file is still drifted after verify returns. Putting it back is punix service rollback; an audit command that mutates the host it audits is not an audit command.
Example¶
$ punix verify WebStack
ok /etc/systemd/system/api.service
MODIFIED /etc/nginx/conf.d/app.conf (recorded 3f2a91cc4b07… now 8e14bd0a72f1…)
missing /etc/app/app.conf (declared, not on the target)
skipped /etc/punix/env/knot.env (artifact-bearing (recorded by reference))
ok /store/a1b2…-caddy-2.9.1
missing /store/c3d4…-postgres-17.2 (pinned store path)
WebStack gen-003: 6 checked, 3 clean, 1 MODIFIED, 2 missing, 1 unverifiable
$ echo $?
1
Exit 0 when the host matches the declaration, 1 when anything drifted, 2 on a usage error.
What each state means¶
| state | meaning | what to do |
|---|---|---|
ok |
the bytes on the target hash to what the generation recorded | nothing |
MODIFIED |
the file exists but its contents changed out of band | service rollback, or redeploy, or accept and redeploy to re-record |
missing |
a declared config file, or a pinned store path whose built marker is gone |
redeploy; a missing store path means the generation cannot actually run |
unreadable |
the transport could not read it (permissions, a broken remote) | an operational problem, not drift |
skipped |
the file cannot be verified by re-hashing; see below | nothing; this is expected |
The two things it cannot check, and why¶
Artifact-bearing files are skipped. A file consuming a cross-host artifact records its sha256 in reference form (the bytes with each digest rendered as a stable {artifact:host/name} token) so that a producer renewing its certificate does not change the consumer's identity (ADR-028 ARTIFACT-REF-HASH). The bytes on disk carry the resolved digest, so re-hashing them would report drift on every healthy host. They are unverifiable by construction; guessing would make the command useless.
Files Punix never wrote are invisible. verify checks the declaration, not the whole filesystem. A stray /etc/nginx/conf.d/rogue.conf that no generation ever mentioned is not drift by this definition: Punix has no opinion about files it does not own.
What it does check that you might not expect¶
Secret-bearing files are verified. A file marked secret = true keeps its payload out of the generation (§20.6) but its hash is still recorded. So drift on exactly the files an operator most wants watched (credentials, keys, env files) is detectable, without the manifest ever having held the secret.
Store paths are checked on the target, through the transport. Validity is the built marker, the same predicate Store.is_valid uses. This is stricter than punix service rollback, which still verifies pinned paths against the deploy-side store; a known gap. verify asks the host.
Options¶
--deployments-root PATH: wheregen-NNN.jsonlives (default~/.punix/deployments).--target ssh://user@host[:port]: verify a remote host. Mutually exclusive with--transport-root.--key/--known-hosts: SSH credentials, as forservice deploy.--transport-root PATH: rebase the filesystem surface (hermetic runs and tests).
Related¶
punix service: deploy and rollback; the command that writes what this reads.- Generations and rollback: the
sha256record this diffs against. - What the target needs: drift detection works on a host with no Punix installed.