Verifying claims without an Anthropic API key¶
Three paths to get the contradicted verdict (or any LLM verdict)
without setting ANTHROPIC_API_KEY. Pick one based on what you have
available. All three are documented surface area; none uses hidden
inheritance.
Decision tree¶
| You have... | Use |
|---|---|
| A local LLM (Ollama, llama.cpp, LM Studio, vLLM) | Local LLM grader (this page §1) |
| Claude Code installed, with hooks enabled | Claude Code Stop hook (this page §2) |
| Claude Desktop with sampling support | MCP sampling (this page §3: design only; deferred to v0.10) |
| None of the above | Heuristic grader (default). Free, deterministic, cannot emit contradicted. |
§1. Local LLM grader¶
The grader speaks the OpenAI-compatible /v1/chat/completions shape
so it works with any of: Ollama, llama.cpp's server, LM Studio, vLLM,
and most local-LLM tools.
Set up Ollama in two commands¶
Point warrantos at it¶
Option A: Environment variables (persistent)
export PROVENANCE_LOCAL_GRADER_URL=http://localhost:11434/v1/chat/completions
export PROVENANCE_LOCAL_GRADER_MODEL=llama3.2
warrantos check examples/quickstart-demo/draft.md \
--context examples/quickstart-demo/context.json \
--actor-identity examples/quickstart-demo/actor.json \
--verify --no-fetch
Option B: CLI flags (one-off)
warrantos check examples/quickstart-demo/draft.md \
--context examples/quickstart-demo/context.json \
--actor-identity examples/quickstart-demo/actor.json \
--verify --no-fetch \
--grader local \
--grader-url http://localhost:11434/v1/chat/completions \
--grader-model llama3.2
Option C: Ollama auto-detection (zero config)
# If Ollama is running on localhost:11434, warrantos detects it automatically
warrantos check examples/quickstart-demo/draft.md \
--context examples/quickstart-demo/context.json \
--actor-identity examples/quickstart-demo/actor.json \
--verify --no-fetch
The verifier now uses your local model. The verifier_rows field in
the report carries grader: local-llm:llama3.2 instead of
heuristic.
Supported local LLM servers¶
Any OpenAI-compatible endpoint works. Here are the tested options:
| Tool | Endpoint | Notes |
|---|---|---|
| Ollama | http://localhost:11434/v1/chat/completions |
Recommended for ease of use. Auto-detected by warrantos. |
| LM Studio | http://localhost:1234/v1/chat/completions |
Desktop app; closed-source but free for personal use. |
| llama.cpp | http://localhost:8000/v1/chat/completions |
CLI tool; most direct control over quantization. |
| vLLM | http://localhost:8000/v1/chat/completions |
GPU-optimized serving; use for high throughput. |
To use an alternative, set PROVENANCE_LOCAL_GRADER_URL to its endpoint:
# LM Studio example (port 1234)
export PROVENANCE_LOCAL_GRADER_URL=http://localhost:1234/v1/chat/completions
warrantos check draft.md --verify --no-fetch
Environment variables¶
| Variable | Default | Purpose |
|---|---|---|
PROVENANCE_LOCAL_GRADER_URL |
(unset) | Required to activate. Full URL to the chat-completions endpoint. |
PROVENANCE_LOCAL_GRADER_MODEL |
llama3.2 |
Model name passed in the request body. |
PROVENANCE_LOCAL_GRADER_API_KEY |
(unset) | Optional Authorization: Bearer ... for self-hosted vLLM behind nginx, etc. Most local servers need none. |
PROVENANCE_LOCAL_GRADER_TIMEOUT |
60 |
Per-call timeout in seconds. |
Cost and data egress¶
| Concern | Reality |
|---|---|
| API cost | 0. No external calls. |
| Data egress | 0. The claim and source text never leave the local host. |
| Anthropic account | Not required. |
| Fallback if endpoint is down | The grader silently falls back to the heuristic; no run breaks. |
Trade-offs¶
- A local model's
contradictedverdicts depend on the model's quality. Llama 3.2 8B-Instruct is competent at basic contradiction; smaller models may miss subtle ones. - The heuristic-vs-local-LLM grader-precision comparison
(
eval/run_eval.py) is the way to calibrate before relying on the local path for high-stakes verification.
§2. Claude Code Stop hook¶
The hooks/claude_code_verify_hook.py script hands unverified
load-bearing claims back to the same Claude Code session that
wrote the draft. The session does the verification using its existing
auth; no separate API key, no separate model, no extra cost.
Wire it in¶
Add to ~/.claude/settings.json (or .claude/settings.json for a
single project):
{
"hooks": {
"Stop": [
{
"type": "command",
"command": "python /path/to/claude-provenance/hooks/claude_code_verify_hook.py",
"blocking": true
}
]
}
}
After installing with pip install claude-provenance, you can also
launch the hook by entry point:
{
"hooks": {
"Stop": [
{
"type": "command",
"command": "warrantos-verify-hook",
"blocking": true
}
]
}
}
(The warrantos-verify-hook entry point is shipped from v0.8.)
What the hook does¶
- Reads the most recent
.warrant/runs/<run_id>/directory. - If
verdict == HOLDand there are load-bearing unsupported claims, prints a structured hand-back message to stderr and exits 2 (which Claude Code reads as feedback for the next turn). - Claude reads the message in the next turn and verifies the claims using the session's own model.
- Loop-safe: if the same hand-back has just been delivered, the hook silently passes through rather than re-blocking.
Cost and credit usage¶
| Concern | Reality |
|---|---|
| Separate API key | Not required. |
| Cost | Whatever your Claude Code session normally costs (Pro / Max plan, or pay-as-you-go API). The hook adds no separate billing. |
| Anthropic account | The one you already use for Claude Code. |
| Privacy | The claims stay in the session you are already running them in. |
Trade-offs¶
- Verification quality depends on the model in your Claude session. An Opus session verifies more accurately than a Haiku session.
- Not all Claude sessions have the context for every domain claim; the hook explicitly asks Claude to flag uncertainty rather than hallucinate a verdict.
§3. MCP sampling (design only; deferred to v0.10)¶
The MCP protocol supports a sampling/createMessage request type: an
MCP server can ask the host (Claude Code, Claude Desktop) to make
an LLM call on its behalf. The host's credentials and billing apply.
The server never sees an API key.
This is the canonical "no separate API key" path for the warrantos
MCP server: when warrant_check is invoked over MCP, the server
would call back through sampling to verify each detected claim.
Status: designed, not implemented in v0.9.0b1. v0.9 shipped empirical calibration on real briefs, INV-004 storage-level append- only triggers, and the per-layer conformance dashboard. MCP sampling implementation requires host-side permission UX work that is still scoped: most current MCP hosts surface each sampling call as an interactive permission prompt, and the unattended trade-off has not been resolved.
The wiring requires:
- A new
MCPSamplingGraderclass that takes a sampling callback. tool_warrant_checkaccepting an optionaluse_mcp_sampling=Trueargument that constructs the callback.- Documentation for the host's sampling permission model (the user must approve each sampling call by default in current Claude hosts; the trade-off vs unattended runs is a real UX decision).
Tracked as a v0.10 deferral in CHANGELOG. Today:
- For interactive Claude Code use, prefer §2 (Stop hook).
- For unattended use without an Anthropic API key, prefer §1 (local LLM).
Summary¶
| Path | API key | Cost | Data leaves local | Verdict quality |
|---|---|---|---|---|
| Heuristic (default) | None | 0 | No (only the URL fetch) | Cannot emit contradicted |
| §1 Local LLM | None | 0 | No | Model-dependent |
| §2 Claude Code hook | None separately | Your Claude session cost | Yes (to Claude session) | Session-model-dependent |
| §3 MCP sampling (deferred, v0.10) | None separately | Your host's session cost | Yes (to host) | Host-model-dependent |
| Anthropic LLM grader | ANTHROPIC_API_KEY |
Per claim, ~USD 0.01 | Yes (to Anthropic) | Highest |
§4. LLM grading is strictly opt-in (hermetic test suite)¶
Every LLM-backed grading path above — local LLM, Claude Code hook,
Claude CLI subscription auto-select, or the paid Anthropic grader — is
an explicit human opt-in activated only by setting the relevant
environment variable (PROVENANCE_GRADER,
PROVENANCE_LOCAL_GRADER_URL, ANTHROPIC_API_KEY). Nothing in
warrantos selects an LLM grader silently, and the automated test suite
never opts in on your behalf:
- The suite is hermetic. Every subprocess a test launches runs
with a scrubbed environment (
tests/conftest.py::get_clean_env()):CLAUDE_HOME,ANTHROPIC_API_KEY,OPENAI_API_KEY, and everyPROVENANCE_*override are removed before the child starts. Tests that need aPROVENANCE_*variable (for example the hook tests settingPROVENANCE_DB) add back exactly the keys they mean to set. - The rule is enforced, not promised. An AST audit
(
tests/test_hermetic.py) fails the suite if any test callssubprocess.run/subprocess.Popenwithout an explicitenv=. - CI booby-traps the
claudebinary. ThehermeticCI job (.github/workflows/ci.yml) puts a shim namedclaudefirst on PATH that logs every invocation and exits 97, runs the whole suite withCLAUDE_HOME/ANTHROPIC_API_KEY/PROVENANCE_*unset, and fails if the shim was called even once. The suite's call count against the shim is zero by construction.
To run LLM grading yourself (calibration, benchmarking, real
verification), set the opt-in variables in your own shell before
invoking warrantos check --verify or eval/run_eval.py --grader ....
Removing the variables returns you to the free offline heuristic.