Skip to content

Repository files navigation

quality-gate

A pay-per-call quality/safety scoring service for AI agents. An agent submits its own output, and the service scores it against a published, public rubric and returns an Ed25519-signed score receipt.

It pairs with provenance-receipts: that service certifies origin; this one certifies quality — specifically, quality as measured by our published rubric.

Scope: We sell a score against our own published rubric, not a claim of absolute truth, not an external/third-party standard, and not a guarantee the content is safe to act on. The rubric is public (src/rubric/quality-rubric.md) and every score is reproducible. Every public claim is verifiable.

What a score receipt proves (and does not)

  • ✅ The content presented at verification is byte-for-byte what was scored (SHA-256 hash match).
  • ✅ The score, rubric version, and timestamp were issued by the holder of this service's signing key (Ed25519 signature check).
  • ⚠️ The score is an LLM judgment against Quality Gate Rubric vX — not a measure of truth or external correctness. "Obvious-error freedom" is not an external fact-check. See the rubric's "Limits" section.

Endpoints

Method Path Purpose Paid?
POST /v1/score Score content against the rubric; signed receipt yes (x402)
POST /v1/verify Re-hash content and check a receipt's signature no, free
GET /v1/rubric The published rubric + current version no, free
GET /v1/public-key Ed25519 public key for independent verification no, free
GET /health Liveness check no, free

Full request/response schemas: docs/API.md. Verify a receipt yourself, without trusting this service: docs/VERIFYING.md.

/v1/score

Input: { content, rubric_version?, target_score? } Output: { score, breakdown, flags, rubric_version, receipt } — the receipt is an Ed25519-signed object committing to { content_hash, score, rubric_version, timestamp, receipt_id, signature }.

"No pass, no pay" mode: if the caller sets target_score, payment is only settled when the score clears it. If it fails, the call is released without charge and returns { ..., passed: false, charged: false, receipt: null } plus the failing breakdown.

Operator-cost note (read this): "No pass, no pay" means the operator absorbs the scoring cost on failed calls. Every /v1/score call runs a paid Claude API pass (~$0.012 on Sonnet 4.6) before the score is known, so when a caller's content misses their target_score we still paid to score it but collected nothing. This is inherent to the feature, not a bug — price the service and your failure-rate expectations accordingly.

The rubric (the heart of the product)

Scoring is a Claude API pass against src/rubric/quality-rubric.md: four dimensions — clarity, completeness, internal consistency, obvious-error freedom — 0–25 each, plus a closed vocabulary of flags. The rubric is versioned and public; receipts record the exact rubric_version scored against.

Payments (x402)

/v1/score is the paid action; /v1/verify, /v1/rubric, and /v1/public-key are free. Payment gating is off by default and only activates when X402_ENABLED="true".

  • Protocol: x402 — a 402 Payment Required response advertises accepts requirements; the client retries with an X-PAYMENT header; a facilitator verifies/settles on-chain.
  • Verifier is pluggable: stub (default — no chain calls, local testing only) or facilitator (free public testnet facilitator at https://x402.org/facilitator, Base Sepolia). The facilitator path is ported but not yet wired — pending a configured testnet wallet.
  • Default price $0.02 (X402_PRICE), set above the ~$0.012 Sonnet 4.6 scoring cost. Nothing here touches mainnet or real funds.

Quickstart (local, free, no payments)

npm install
npm run build:rubric                      # embed the rubric .md into a module
node scripts/gen-dev-key.mjs --write      # writes SIGNING_PRIVATE_KEY to .dev.vars (gitignored)
# add ANTHROPIC_API_KEY="sk-ant-..." to .dev.vars for real scoring (paid)

npm test          # 20 tests, fully mocked — no API calls, $0
npm run typecheck
npm run dev       # wrangler dev on http://localhost:8787

GET /v1/rubric, GET /v1/public-key, and POST /v1/verify are free. A real POST /v1/score makes a paid Claude API call (~$0.012/call).

Stack (same proven plumbing as provenance-receipts)

  • Cloudflare Workers + Wrangler, Hono routing
  • Ed25519 via @noble/ed25519ported, not reinvented, from provenance-receipts
  • Cloudflare KV for receipt records
  • Anthropic API for the scoring pass
  • x402 micropayments (USDC on Base) — built last, stub verifier first

Build status

  • Step 1 — skeleton + structure + published rubric draft
  • Step 2 — /v1/score + /v1/verify with signing & rubric scoring (no payments)
  • Step 3 — x402 payment layer (stub verifier) + "no pass, no pay"
  • Step 4 — README + public docs + public rubric page; self-verified claims (API.md, VERIFYING.md)
  • Real Base Sepolia testnet wiring (FacilitatorVerifier) — verified end-to-end on-chain (2026-06-12): 402 → buyer signs → score → settle → 200; 0.02 test USDC moved buyer → seller in tx 0xb4973438…52c5a91a. Testnet only — no mainnet, no real funds.

Project layout

quality-gate/
├── src/
│   ├── index.ts            # Hono app entry + route mounting
│   ├── types.ts            # Env, ScoreReceipt, ScoreResult, ScoreBreakdown
│   ├── routes/
│   │   ├── score.ts         # POST /v1/score   (step 2)
│   │   └── verify.ts        # POST /v1/verify  (step 2)
│   ├── lib/                 # PORTED from provenance-receipts (steps 2–3)
│   │   ├── crypto.ts         # Ed25519 + SHA-256
│   │   ├── receipt.ts        # score-receipt build/verify
│   │   └── kv.ts             # KV storage helpers
│   ├── scoring/
│   │   ├── rubric.ts         # rubric loading + versioning
│   │   ├── rubric-text.ts    # AUTO-GENERATED from the .md (npm run build:rubric)
│   │   └── scorer.ts         # Anthropic scoring pass (pinned: claude-sonnet-4-6)
│   ├── rubric/
│   │   └── quality-rubric.md # THE PUBLIC RUBRIC (v1) — single source of truth
│   └── payments/            # x402 layer, ported from provenance-receipts
│       ├── types.ts · stub-verifier.ts · facilitator-verifier.ts
│       ├── config.ts        # env-driven config + verifier selection
│       └── gate.ts          # 402 helper for the verify→score→settle flow
├── docs/                    # API.md, VERIFYING.md
├── scripts/                 # embed-rubric · gen-dev-key · verify-receipt
├── test/                    # 20 tests (mocked scorer + stub verifier)
├── package.json
├── tsconfig.json
├── wrangler.toml
├── .gitignore
└── .dev.vars.example       # copy to .dev.vars locally; never commit secrets

Security / cost ground rules (same as provenance-receipts)

  • No secrets in code. SIGNING_PRIVATE_KEY and ANTHROPIC_API_KEY are set with wrangler secret put. .dev.vars / .testnet.vars are gitignored.
  • Nothing here costs money without confirmation. Everything is built/tested free/local/testnet first. Any step that would incur cost (a domain, a paid Cloudflare tier, Base mainnet gas, large Anthropic API spend) is confirmed first. The scoring pass does call the paid Anthropic API — local testing uses small inputs and is confirmed before running.
  • Every documented claim is verifiable. The rubric is public; scores are reproducible; receipts are independently checkable.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages