Know your coding agent hasn't changed behind your back.
Claude Code is not just a model. It is a model plus everything you have wired
around it: skills in ~/.claude/skills, an allow/deny permission policy, MCP
servers, your CLAUDE.md and memory, and the tools it can call. That whole
composition decides what your agent can do to your machine and your code. Any of
it can change without you noticing.
This plugin answers one question at the start of every session:
Is the agent I'm running the one I approved: nothing added, nothing subtracted?
You approved a setup you trust. Then, quietly, things drift:
- A skill you installed ships an update that now runs
curlto an address you never saw. - A dependency's postinstall drops a
SKILL.mdinto~/.claude/skills. - A permission gets widened from
Bash(git:*)toBash(*)during some debugging session and never gets narrowed back. - An MCP server you added for one task is still connected weeks later.
- Your
CLAUDE.mdpicks up an instruction you didn't write.
None of these announce themselves. Each one changes what your agent will do on your next run. This plugin fingerprints the whole composition, stores an approved baseline, and tells you at session start the moment any of it moves. It is the difference between "I think my agent is what I set up" and "I can prove it, and I'd know within one session if it wasn't."
# 1. add the marketplace and install the plugin (hooks + commands)
/plugin marketplace add agentrust-io/integrations
/plugin install agentrust-claude-codeThat's the whole install for drift detection. The SessionStart hook is
needs one package, agentrust-capture-core, which itself has no dependencies, so
the install stays a single lightweight step rather than a tree. Without it the hook
tells you what to install rather than silently skipping the check.
On your first session after install, it records your baseline and tells you:
AgenTrust: baseline established for this Claude agent (7 skills, 2 MCP on disk).
Future sessions are checked against it. Run /manifest approve to re-baseline.
The baseline lives at ~/.claude/agentrust/baseline.json. From then on, every
session is checked against it.
Signed records (/trace and /manifest approve --sign) are the only feature
that needs crypto packages. Install them when you want them:
pip install -r claude-code/requirements.txtYou mostly do nothing. You install it, and it stays quiet until something changes. When it does, one line shows up at session start:
AgenTrust WARNING: 1 change(s) to your agent since baseline: added skill
pypi-helper. Run /manifest verify for detail, or /manifest approve to accept.
Two responses, both one command:
If the change is a surprise, look at it. /manifest verify re-reads your
setup right now and lays out exactly what moved:
NOTHING ADDED, NOTHING SUBTRACTED? (vs approved baseline)
--------------------------------------------------------------
~ CHANGED permissions: policy_bundle
+ ADDED skill: exfil
>> 2 change(s) since baseline. Review above.
Now you decide with the facts in front of you: remove the rogue skill, narrow the permission, or accept it.
If you made the change on purpose (installed a skill you wanted, added an MCP server for real work), tell the plugin this is the new normal:
/manifest approve
That promotes your current setup to the approved baseline. The warnings stop until something moves again.
| Command | What it does | Needs crypto packages? |
|---|---|---|
| SessionStart hook | Snapshot the agent from disk, diff against your approved baseline, warn in-session on drift | No |
/manifest verify |
Re-snapshot now and show the full diff, including the live tool and MCP roster the agent reports this session | No |
/manifest approve |
Make the current composition the approved baseline | No (add --sign for records) |
/manifest show |
Show the current composition without touching the baseline | No |
/trace |
Build and sign the Agent Manifest and TRACE record for this session, explained in plain English | Yes |
/manifest verify always re-reads your setup fresh, so it catches drift that
happens partway through a session, not just at startup.
It records fingerprints, never raw content, never secrets:
- skills: each
~/.claude/skills/*/SKILL.md - permissions:
~/.claude/settings.json(the allow/deny policy) - instruction layer: your
CLAUDE.mdand memory tree - tools and MCP servers: the roster the agent reports, by name only
- model: provider, id, version
It never reads ~/.claude/.credentials.json. It records skill, tool, and MCP
names only, never tokens, never environment values, never file contents.
A changed fingerprint tells you that something changed and which category,
which is what you need to go look.
When you run /trace (or /manifest approve --sign), the plugin writes three
files:
manifest.json, an Agent Manifest: what your agent is, the full composition above, each part fingerprinted and Ed25519-signed (agent-manifest).trace.json, a TRACE Trust Record: what your agent did this run, signed and checkable against the public conformance suite (trace-spec).verification_key.json, the public key for the manifest.
These are shareable proof a third party can verify without trusting your machine.
The manifest is signed with a persistent key kept at
~/.claude/agentrust/signing_key.json. It is generated once, reused every run,
and its private half never leaves your machine, so every record you produce
carries the same signing identity. verification_key.json publishes only the
public half ({key_id, public_key_b64url}).
Confirm the TRACE record against the conformance suite:
trace-tests verify --record trace.json --level 0
# or, if the console script is not on PATH:
python -m trace_tests.cli verify --record trace.json --level 0Verify the Agent Manifest on any machine, using only the published public key:
import json
from agent_manifest import VerificationContext, verify_manifest, RevocationStore
manifest = json.load(open("manifest.json"))
vk = json.load(open("verification_key.json"))
ctx = VerificationContext(trusted_keys={vk["key_id"]: vk["public_key_b64url"]})
result = verify_manifest(manifest, ctx, RevocationStore())
print(result.result, result.signature_verified) # VALID True (any tampering -> MISMATCH False)This plugin is honest about what it is. On a normal developer machine:
-
Software-only, Level 0. A dev box has no hardware TEE, so the TRACE record is software integrity, not silicon-rooted attestation. It is labelled Level 0, never presented as hardware-attested.
-
The instruction layer is a proxy. The
system_promptfingerprint covers yourCLAUDE.mdand memory tree, not Claude Code's internal system prompt, which is not on disk. -
policy_languageis modelled ascomposite. The agent-manifestpolicy_languageenum (cedar/rego/yaml-agt/composite) has no value for host-native permission systems like Claude Code'ssettings.json. A spec value for host-native permissions is proposed upstream. -
The hook sees disk, commands see the session. A shell hook cannot enumerate the live tool roster, so the SessionStart check compares skills, permissions, and the instruction layer. The full tool and MCP diff runs in
/manifest verify, where the agent supplies the live roster. -
The live roster is self-reported. In
/manifest verifythe agent supplies its own model, tools, and MCP servers. Nothing verifies that report, so an agent motivated to hide a connected server could simply omit it. Closing this needs an observer outside the agent and cannot be done from inside it. -
The baseline seal catches accidents, not adversaries.
baseline.jsoncarries a SHA-256 digest of its own content, so corruption, truncation, and a hand-edit that does not recompute it are all caught. Anyone who owns~/.claude/agentrustcan recompute the digest as easily as the tool can, so the local check is tamper evidence against accident and carelessness, not tamper proofing against a compromised home directory. It is not presented as the latter.We tried the stronger-looking version first, an HMAC with a local secret, and removed it. The only adversary an HMAC defeats here is one who can write that directory without being able to read it, which barely exists on a developer machine, and the stored secret was a credential to leak in exchange.
The mitigation that does survive a real adversary is off-box:
approveprints the baseline digest andverifyprints the digest of the baseline it read. Record the first elsewhere and compare. A silent re-baseline changes the digest even when the attacker resealed it perfectly. Anchoring the digest in an append-only log would automate that comparison and is the natural next step.
claude-code/
.claude-plugin/plugin.json plugin manifest
hooks/hooks.json SessionStart -> engine/capture.py hook
commands/manifest.md /manifest verify | approve | show
commands/trace.md /trace report
engine/capture.py capture engine (stdlib hook + signing report)
tests/test_capture.py stdlib-only tests
integration.yaml agentrust-io integration manifest
requirements.txt crypto deps for signing only
Apache-2.0. Part of the agentrust-io open agent-governance toolchain.