Skip to content

Run a verifier

The verifier is a small HTTP service in Pactwright-Kit. It turns evidence into a signed verdict. It holds no money; its only power is its signing key, so treat that key with care.

The process refuses to start, listing every problem at once, if anything required is missing. It never invents a model reading.

Variable Required What it is
VERIFIER_SECRET_KEY Yes The Stellar secret seed that signs envelopes and attestations. Its public key must be the one the pact contract and the sellers name
VERIFIER_PROVIDER Yes The model provider. Recorded in every envelope
VERIFIER_MODEL Yes The pinned model ID. There is no default model
VERIFIER_LLM_API_KEY Yes The provider’s API key
LLM_BASE_URL No The provider’s OpenAI-compatible base URL, if it isn’t the adapter’s default. For Google: https://generativelanguage.googleapis.com/v1beta/openai
VERIFIER_EFFORT No Reasoning effort, where the provider supports it. Recorded in every envelope
VERIFIER_MODEL_TIMEOUT_MS No Per-request model timeout (default 60 seconds). Rate limits and overloads are retried
EVIDENCE_BUCKET_URL Yes An https prefix the verifier can read evidence from. Evidence outside it is refused before any fetch
STELLAR_RPC_URL Yes A Soroban RPC. Each escrow attestation is stamped with its latest ledger, which the contract requires
FACILITATOR_URL Yes The x402 facilitator, for submitting verdicts
VERIFIER_PUBLIC_URL Yes, unless on Render Where this verifier is reachable; envelope links point at <url>/v1/envelopes/<sha256>. On Render, RENDER_EXTERNAL_URL is used when this is unset
DATABASE_URL (or VERIFIER_DATABASE_URL) In production Postgres for verdict envelopes, so they survive restarts. With NODE_ENV=production (as in the Docker image) the verifier refuses to start without it
PORT / VERIFIER_PORT No Listening port; VERIFIER_PORT wins, then the host’s PORT, then 8787
Terminal window
docker build -f services/verifier/Dockerfile -t pactwright-verifier .
docker run -p 10000:10000 \
-e VERIFIER_SECRET_KEY=S… \
-e VERIFIER_PROVIDER=google -e VERIFIER_MODEL=gemini-3.5-flash-lite \
-e LLM_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai \
-e VERIFIER_LLM_API_KEY=… \
-e EVIDENCE_BUCKET_URL=https://raw.githubusercontent.com/Pactwright/pactwright-evidence/main/ \
-e STELLAR_RPC_URL=https://soroban-testnet.stellar.org \
-e FACILITATOR_URL=https://pactwright-facilitator-testnet.onrender.com \
-e VERIFIER_PUBLIC_URL=http://localhost:10000 \
-e DATABASE_URL=postgres://… \
pactwright-verifier
curl http://localhost:10000/health

/health returns the verifier’s public key, both as a Stellar address and as the hex that sellers put in their terms. Check it matches before pointing anyone at your verifier.

The Kit’s render.yaml describes the hosted testnet verifier and facilitator. In Render, choose New → Blueprint, select the repository, and fill in the secret values when asked. They stay in Render’s secret store.

Evidence files are addressed by their content: the request lists each file’s URI and SHA-256, the verifier fetches it from under EVIDENCE_BUCKET_URL and refuses it if the hash doesn’t match. On testnet, the public Pactwright/pactwright-evidence repository serves as the store, laid out as <pact_id>/<sha256>. It’s public, so it’s for testnet only.

services/verifier/eval runs the labelled cases through the full pipeline with your configured model and reports accuracy per condition kind. The verifier-eval workflow fails if it drops below 90%.

We never DM first; there is no token or airdrop. Security issues go to a repo's SECURITY.md, not chat. Testnet only — not audited.