The security model, plainly
No product is unbreakable, and Verfy does not claim to be. This page says what Verfy protects, how, and where its protection ends. The full threat model is in docs/THREAT_MODEL.md in the repository.
proves A receipt shows
- which tool call passed through Verfy, when, and in which session;
- the verdict, the rules that fired and the hash of the policy that decided it;
- the digest of the tool definition in use, and a salted hash of the arguments and result, so nobody can guess them from the receipt;
- that the device holding the key signed it, and that no receipt in the chain was changed, removed or reordered since.
does not prove A receipt cannot show
- what a tool or MCP server did internally after it was called;
- actions that never went through the Verfy hook or gateway;
- that the device itself was not compromised when it signed;
- that a whole ledger was not deleted and restarted, or rebuilt and re-signed by someone holding the device key (only an outside copy, such as the receipts already synced to Verfy Cloud, shows that).
Controls in place
Tenant isolation
Row-level security on every table, keyed to the signed-in user's memberships. Server routes check the organization again. Cross-tenant access is covered by automated tests on real Postgres.
No direct writes
Browsers can only read. Every change goes through a database function that checks the caller's role and writes an audit entry in the same transaction.
Append-only evidence
Receipts, published policy versions and the audit log cannot be updated or deleted, not even with the server's secret key.
Device keys
Receipts are signed on the device with Ed25519. The server stores only public keys and re-verifies every signature and hash link on upload.
Secrets
The secret Supabase key is used only in server code after Verfy has authenticated the caller. Device tokens and invitation tokens are stored as SHA-256 hashes.
Operator access
Verfy's operator has one read-only page listing accounts, organizations and the waitlist (no receipt contents or policies). It opens only for the operator's account after a second factor; everyone else gets a 404, and every view is recorded in an append-only log before anything is read.
Web hardening
A CSP with no inline script it does not name (a per-request nonce on signed-in pages, a SHA-256 for each script on the public pages), HSTS, frame blocking, strict referrer policy, origin checks on every cookie-authenticated change, schema validation with size limits, and safe error messages.
Rate limits
Shared Postgres counters across serverless instances for sign-in, enrollment, ingest, exports and public endpoints. If the limiter fails, requests are refused, not let through.
Supply chain
Pinned dependencies with a lockfile, dependency audit and secret scanning in CI, and a CLI with no required third-party packages.
Can the agent switch Verfy off?
We tried the obvious ways. Verfy blocks the agent from editing its hook settings, its lock, its policy or its install, and from approving a tool change for itself. It cannot follow a program the agent writes to a file and then runs: that program can still change the settings. Verfy also cannot notice from inside the computer that its hook was removed, because a removed hook does not run; on a team, the device simply stops sending receipts. Checking the settings again at session start would not change this, since that check would live in the same settings file.
| What the agent tries | Verdict | Rule |
|---|---|---|
Edit ~/.claude/settings.json to remove the hook | deny | VFY-SELF-001 |
printf '{}' > .claude/settings.json | deny | VFY-SELF-001 |
rm ~/.claude/settings.json | deny | VFY-SELF-001 |
verfy lock approve filesystem --yes | deny | VFY-SELF-003 |
pip uninstall -y verfy | deny | VFY-SELF-004 |
Edit .mcp.json to take a server off the gateway | ask | VFY-SELF-002 |
python -c "open('.claude/settings.json','w').write('{}')" | ask | VFY-SELF-005 |
cat ~/.verfy/key.json (the signing key) | deny | VFY-SELF-006 |
Read ~/.verfy/team.json (the device token) | deny | VFY-SELF-006 |
Write fix.py, then run python fix.py | allow | not followed |
Known limits
- Guard rules are pattern matching. A determined attacker can phrase a command Verfy does not recognise; unknown risky patterns are not assumed safe, but Verfy is not a sandbox.
- The scanner finds known warning signs. A clean scan does not mean a tool is safe.
- DDoS protection depends on your hosting plan and settings. Verfy adds application rate limits; it does not replace a firewall.
- No independent penetration test has been performed yet. Treat this release as an MVP and review it before relying on it.
Find a bypass
Verfy has not had a professional penetration test yet, so we ask everyone to try to break it: a command, path, URL or tool call that should be denied or asked about but is allowed, a way for an agent to switch Verfy off, a changed MCP tool that gets past the gateway, or an edited receipt chain that still verifies. verfy check "…" shows the verdict without running anything.
Report it privately (below). With your permission, everyone who finds a valid bypass is listed in the hall of fame and credited in the release notes. Known limits, such as a script the agent writes and then runs, are listed above and are not bypasses.
Report a vulnerability
Please report security issues privately, not in public issues. Use GitHub's private vulnerability reporting on the Verfy repository (Security tab, "Report a vulnerability": open the form). A way for an agent to get past Verfy counts as a vulnerability too. Include steps to reproduce and the version you tested. We will acknowledge your report and keep you informed while we fix it.
Please do not test against other people's deployments, access data that is not yours, or degrade service for others.