Quickstart
Guard a coding agent on your machine first. Connecting to Verfy Team is optional and can come later.
1. Install
Python 3.10 or newer. No other dependencies (install the optional cryptography extra for faster signing).
$ pip install verfy
$ pip install "verfy[fast]" # optional: faster signing
$ verfy init
$ verfy check "git push --force" # exit 1: needs approval2. Guard Claude Code
The hook checks every tool call before it runs and writes a signed receipt. Review the change first (without --write it only prints it).
$ verfy hook install # print the settings change
$ verfy hook install --write # save it (backup kept)
$ verfy ledger show
$ verfy verify3. Scan and lock MCP servers
$ verfy scan --discover # static: how servers are launched
$ verfy wrap ~/.cursor/mcp.json --write # put servers behind the gateway
$ verfy lock status # after first use, tools are locked
$ verfy lock diff filesystem # if a definition drifted
$ verfy lock approve filesystem # review, then accept4. Fail the pull request when a tool changes
Lock the servers your repository configures and commit the lock. In CI, verfy lock check starts each stdio server from .mcp.json, reads its tools and fails if any definition differs from verfy.lock. The diff appears in the log and as an annotation on the pull request.
$ verfy lock check --update # shows each definition and its scan findings, then writes verfy.lock
$ git add verfy.lock .mcp.json && git commit -m "Lock MCP tools"name: Verfy Lock
on: [pull_request, push]
permissions:
contents: read
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.13"
- uses: anvarbekovjavohir277-dot/verfy@main # not public yet; see the note below
with:
config: .mcp.json
lock: verfy.lockThe check runs the server commands in your config, as your MCP client would, so pin package versions and pass any keys they need as CI secrets. Remote (HTTP) servers are skipped. The action lives in the Verfy repository, which is not public yet; until it is, copy action.yml and verfy-cli/ into your own repository and use uses: ./.
Export receipts as SCITT signed statements
Each receipt can be written as a COSE_Sign1 signed statement with CWT claims, the envelope the IETF SCITT architecture (an Internet-Draft) registers with a transparency service. Anyone with your public key can check one offline. Verfy does not run a transparency service.
$ verfy scitt export --out statements/ --issuer https://acme.example/devices/laptop-1
$ verfy key --json > signer.json
$ verfy scitt verify statements/*.cose --pub signer.json5. Connect to Verfy Team
In the dashboard, open Devices → Add device to get a one-time code (valid for one hour). Only the device's public key is sent.
$ verfy team enroll --url https://verfy.example.com --code atle_…
$ verfy sync # run it from cron or CI; never inside the hook
$ verfy team pull # team policy and approved locksPolicy
JSON merged over the defaults. Order of precedence: ./verfy.policy.json, then the team policy from verfy team pull, then ~/.verfy/policy.json.
{
"on_ask": "deny",
"fail_mode": "ask",
"tools": {
"deny": [
"github/delete_*"
],
"ask": [
"merge_pull_request"
]
},
"network": {
"allow": [
"github.com",
"*.python.org"
],
"unknown": "ask"
},
"rules": [
{
"id": "no-prod-db",
"pattern": "prod-db",
"verdict": "deny",
"message": "Production is off-limits."
}
],
"lock": {
"on_new": "ask"
},
"receipts": {
"include_args": false
}
}CLI reference
| Command | What it does | Exit codes |
|---|---|---|
verfy init | Create a signing key and a starter policy in ~/.verfy | 0 |
verfy scan --discover | Static check of the MCP servers in your Claude Code, Cursor and Claude Desktop configs | 0 ok · 1 medium · 2 high or worse |
verfy scan --discover --live | Also start each stdio server and scan its tool definitions | 0 · 1 with --fail-under |
verfy scan FILE | Scan a saved tools/list answer | 0 · 1 with --fail-under |
verfy check "CMD" | Pre-check one command or --tool NAME --args JSON without running it | 0 allow · 1 ask · 2 deny |
verfy hook install --write | Add the PreToolUse hook to ~/.claude/settings.json (--project for this repo) | 0 |
verfy hook pre-tool-use | The hook entry point Claude Code calls (JSON on stdin and stdout) | always 0 |
verfy proxy --name N -- CMD | Run an MCP server behind the Verfy gateway | server's exit code |
verfy wrap CONFIG --write | Put every server in an MCP config behind the gateway (keeps a backup) | 0 |
verfy lock check | Start the servers in .mcp.json and compare their tools with ./verfy.lock (for CI); --update writes reviewed changes | 0 ok · 1 new or removed · 2 drift · 3 a server did not answer |
verfy lock status | diff | approve | add | verify | Inspect and approve tool definition changes in verfy.lock | status: 0 ok · 1 new · 2 drift |
verfy verify | Verify every signature and hash link in the local ledger | 0 ok · 1 failed |
verfy ledger show | List recent receipts | 0 |
verfy scitt export --out DIR | Write each receipt as a SCITT signed statement (COSE_Sign1) | 0 |
verfy scitt verify FILE… | Check statements: COSE signature, subject and the receipt inside | 0 ok · 1 failed |
verfy team enroll --url URL --code CODE | Register this device's public key with your team | 0 · 4 on a team error |
verfy sync | Upload new receipts to Verfy Team | 0 · 4 on a team error |
verfy team pull | Fetch the team policy and team-approved locks | 0 |
verfy team propose SERVER TOOL | Ask admins to approve a changed tool definition | 0 |
API
JSON over HTTPS. Errors use RFC 9457 application/problem+json. Full request and response schemas are in docs/API.md.
| Method | Path | Who | Purpose |
|---|---|---|---|
| POST | /api/v1/devices/enroll | enrollment code | Register a device public key; returns a device token once |
| POST | /api/v1/receipts/batch | device token | Upload up to 500 signed receipts (idempotent) |
| GET | /api/v1/policies/current | device or member | Current team policy |
| PUT | /api/v1/policies/current | owner, admin | Publish a policy (simulator runs first) |
| POST | /api/v1/policies/simulate | member | Compare a proposed policy with the current one |
| GET | /api/v1/locks | device or member | Team locks (?status=approved) |
| POST | /api/v1/locks/propose | device token | Propose a tool definition |
| POST | /api/v1/locks/{id}/approve · /reject | owner, admin | Decide a pending lock |
| GET | /api/v1/receipts | member | Search receipts |
| GET | /api/v1/chains/{chain}/verify | member | Re-verify a device chain on the server |
| POST | /api/v1/exports | owner, admin, auditor | Build an evidence export |
| GET | /api/v1/exports/{id} | owner, admin, auditor | Export status and 60-second download links |
| GET | /api/v1/advisories | public | Verfy Advisories feed |
Authorization: Bearer atd_…. Session endpoints need the signed-in cookie and an org parameter.