POST /v1/verify-facts resolves a claim with a deterministic check and signs the result. No LLM is invoked anywhere in the pipeline. The receipt records check_mode: "deterministic" so a verifier can prove that — not take our word for it.
The reasonable objection to any verification service is: who checks the checker? If an LLM decides whether a claim is true, the trust chain contains a model, and the model is the weakest link in an audit.
Some claims do not need judgment. "Does this signature verify against this key" is not an opinion. "Does this package exist in PyPI at this version" is a lookup. "Is this timestamp inside this window" is arithmetic. For that class, the honest answer is to run the check and sign the result — with nothing probabilistic in the path.
What the mode claims: no LLM in the trust chain, recomputable end-to-end, same envelope and same verifier as every other AgentOracle receipt.
What it does not claim: that the underlying source is correct. A deterministic check proves what was checked and what the check returned — not that a registry, a key, or a schema is itself trustworthy. It also does not make the caller's agent AI-free; the agent may well have used a model to generate the claim. The guarantee is scoped to our check.
This list is not written into the page. It is fetched from GET /v1/verify-facts when you load it — the same endpoint that serves the checks. If the catalog and this page ever disagree, the catalog is right and this page is broken.
| Check type | Resolves | Required input |
|---|---|---|
signature_verification |
Does this signature verify against this key? Ed25519 / EdDSA. | signing_input_b64u, signature_b64u, public_jwk |
hash_comparison |
Does the claimed digest match a digest computed over the content? SHA-256 / SHA-512, timing-safe compare. | claimed_digest, and content or content_b64u |
registry_lookup |
Does this entry exist in a declared authoritative registry, with matching metadata? Allowlisted registries only. | registry, identifier, optional expect |
regex_match |
Does this value match a declared pattern? | value, pattern, optional flags |
timestamp_validation |
Does this timestamp fall inside declared bounds? | timestamp, and not_before and/or not_after |
json_schema_conformance |
Does this document conform to a declared schema? Documented keyword subset; unsupported keywords are rejected, never skipped. | document, schema |
$ curl -sS https://agentoracle.co/v1/verify-facts \
-H "authorization: Bearer $AO_API_KEY" \
-H "content-type: application/json" \
-d '{
"subject": { "claim_hash": "sha256-…" },
"checks": [
{
"check_type": "hash_comparison",
"input": {
"content": "the exact bytes being attested",
"claimed_digest": "sha256-…"
}
},
{
"check_type": "registry_lookup",
"input": {
"registry": "pypi",
"identifier": "agentoracle-receipt-verify"
}
}
]
}'
{
"jws": { "payload": "…", "signatures": [ … ] },
"canonical_sha256": "sha256-…",
"verdict": "act",
"check_mode": "deterministic",
"check_types_applied": ["hash_comparison", "registry_lookup"],
"check_results": [ { "check_type": …, "outcome": "pass", "evidence": { … } } ]
}
Inside the signed payload, check_mode: "deterministic" and check_types_applied[] sit under the signature, so they cannot be added or removed after issue without breaking verification. v_gate.confidence is 1.0 by construction and v_gate.v_adversarial_result is "n/a" — there is no judgment to contest and no probability to report.
422 claim_not_deterministicIf a claim cannot be resolved by a shipped check type, the endpoint refuses it. There is no fallback to the judgment path. A silent escalation to /v1/compose would mean a caller who asked for a deterministic receipt could receive a model-judged one without knowing — which would make check_mode worthless as a signal.
HTTP/1.1 422 Unprocessable Entity
{
"error": "claim_not_deterministic",
"reason": "Claim cannot be resolved by any deterministic check type…",
"supported_check_types": [ … ],
"llm_fallback": false
}
halt here would assert "this claim is false" when the truth is "we could not check." A receipt must never misrepresent an inability to check as a negative finding.Move to POST /v1/compose or POST /evaluate if you want judgment. That has to be an explicit decision by the caller.
A caller cannot pass an arbitrary URL. Beyond the obvious request-forgery surface, an open fetch would destroy recomputability: a caller could point the check at a server they control and obtain a signed receipt attesting whatever that server returned. Each lookup pins a response_sha256 and observed_at in the evidence, so the receipt stays auditable after the upstream registry changes.
json_schema_conformance ships a documented subset. A schema using keywords outside it returns 422 rather than validating against the part we understand. Silently skipping unknown keywords would issue a signed receipt attesting a check that was never performed.
Deterministic-mode receipts are canonicalized and signed by the same RFC 8785 JCS implementation and the same Ed25519 key as every other AgentOracle receipt. There is exactly one canonicalizer in the process — a second copy would silently diverge from the reference verifiers in the conformance suite.
Nothing changes on the verifier side. Same envelope, same published JWKS, same reference verifier.
$ pip install agentoracle-receipt-verify
$ python3 -c "import json,sys; from agentoracle_receipt_verify import verify; print(verify(json.load(open('receipt.json'))))"
✓ valid: True — signature verifies against published JWKS (offline)
To confirm the mode independently, decode the payload and read check_mode yourself. It is inside the signed bytes; if it says deterministic and the signature verifies, no model was in the trust chain for that decision.
Conformance vectors for each check type — including the rejection cases, which must never produce a receipt — ship in the receipt-spec repo so a second implementer can byte-match the deterministic path the same way the judgment path is already matched.
Deterministic mode is a capability of the existing tiers, not a separate product. Self-Serve keys and x402 pay-per-call both reach POST /v1/verify-facts with no change to your credentials. Verification is free and offline, as it is for every AgentOracle receipt. See pricing.