aspex lock
Records the security-relevant state of the environment in.aspex.lock:
Deterministic: re-locking an unchanged setup produces a byte-identical file. Sorted keys, no timestamps, no scores. Schema-versioned (
aspex-lock/v1); a newer schema is rejected rather than misread. No secret values, ever. Commit it.
--no-exec locks from configs alone (capabilities inferred from well-known packages, tool lists empty); a live lock captures tool descriptions and is what makes rug-pull detection possible.
aspex verify
Compares the environment now with the lock. Every change is classified from its content:
New attack paths are always shown with their evidence, and the blast radius is printed before → after.
--fail-on suspicious | security-relevant | informational | off (default security-relevant). --verbose shows informational changes. --json is stable.
aspex-scan verify <package> used to look a package up in the known-malicious registry. That is now aspex-scan check-package; the old spelling still routes there with a note.aspex diff
A security impact diff, not a config diff. It answers: what changed in what the agent can do, what it can reach, and which attack paths exist.
Real output, a commit that widened a filesystem root to the home directory, added a browser server and a hook:
Pull requests
The reference action runs the revision diff on every PR, posts (and updates) one Markdown comment, and fails on a drift class:worst and attack-paths-added for further steps. --json and --markdown make the same data available to any other CI.
The comparison engine
All of the above use one function,agentenv.Compare(before, after), over one model (agentenv.Environment), so a change reads identically whether it came from a lockfile, a git revision, --watch, aspex history, or the read-only MCP tool. Git is one file source among others; the engine does not depend on it.