Verified marketplace goods
Your agent writes these files into openspec/changes/integrate-aisot-verified-listings/ in your repo, runs openspec validate integrate-aisot-verified-listings, and works the task list. The setup guide explains each step for people.
language: http · python · typescript with: PromptReports
OPENSPEC/CHANGES/INTEGRATE-AISOT-VERIFIED-LISTINGS/PROPOSAL.MD
# Proposal: Verified marketplace goods
## Why
Buyers have to trust the seller's own quality claims. A verification record states which claims were checked, under which judge, and the outcome, and it outlives the store.
## What Changes
- The marketplace verifies each item's claims against their sources with a judge model and prompt pinned by hash.
- It publishes a verification record per item, labeled VERIFIED.
- If the judge turns out to have a blind spot, its hash names every affected record for re-verification.
## Capabilities
### New Capabilities
- `verified-listings`: A verification record per item, scored under a judge pinned by hash. Buyers check without trusting the seller.
## Impact
- External service: AI Source of Truth at https://aisourceoftruth.com. Reads are anonymous. Writes need a publisher key pair you generate, a client id and secret from registration, and a token from `/oauth/token`.
- Routes used: `POST /v1/records`, `POST /v1/verify-batch`, `GET /v1/judges/{hash}`.
- No PromptReports product is required.
- Source of this plan: https://aisourceoftruth.com/use-cases/verified-marketplace (CC0). Regenerate it with `curl -H 'Accept: application/json' 'https://aisourceoftruth.com/use-cases/verified-marketplace/openspec?lang=typescript'`.
OPENSPEC/CHANGES/INTEGRATE-AISOT-VERIFIED-LISTINGS/DESIGN.MD
# Design: Verified marketplace goods
## Context
Marketplaces for datasets, reports, prompts and models. Buyers have to trust the seller's own quality claims. A verification record states which claims were checked, under which judge, and the outcome, and it outlives the store.
## Where it sits in your agent
Publish from the step that approves a listing. Keep the judge prompt versioned and hashed in your repo.
Over MCP, point the agent at `https://aisourceoftruth.com/mcp` (Streamable HTTP, JSON-RPC 2.0; read tools need no token). The tools this change uses: `publish_record`, `verify_batch`.
## Decisions
- Treat the registry as a source of facts about records, never about the world. Show what it says, with the digest, and do not turn a verdict into a quality score.
- If the registry cannot be reached, treat the answer as unknown (the same as `check`), never as `rely`, and retry later.
- Keep content private: send hashes. Never put personal data in a record; it is public and permanent.
- Keep the private key and client secret in your secret store. Rotate keys at `POST /v1/keys`; a revoked key cannot sign.
## Setup and signing helper
```ts
// Node 20 or later: WebCrypto signs Ed25519 with no dependency.
const AISOT = process.env.AISOT_ORIGIN ?? "https://aisourceoftruth.com";
const key = await crypto.subtle.importKey("jwk", JSON.parse(process.env.AISOT_PRIVATE_JWK!), { name: "Ed25519" }, false, ["sign"]);
async function token(): Promise<string> {
const r = await fetch(`${AISOT}/oauth/token`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ grant_type: "client_credentials", client_id: process.env.AISOT_CLIENT_ID, client_secret: process.env.AISOT_CLIENT_SECRET }),
});
if (!r.ok) throw new Error(`token: ${r.status}`);
return (await r.json()).access_token; // lasts 60 minutes
}
async function signedPost(path: string, body: unknown, contentType = "application/json"): Promise<any> {
const raw = new TextEncoder().encode(JSON.stringify(body));
const sig = Buffer.from(await crypto.subtle.sign("Ed25519", key, raw)).toString("base64url");
const r = await fetch(`${AISOT}${path}`, {
method: "POST",
headers: {
"content-type": contentType,
authorization: `Bearer ${await token()}`,
"x-aisot-key-id": process.env.AISOT_KEY_ID!,
"x-aisot-signature": sig,
"idempotency-key": crypto.randomUUID(),
},
body: raw,
});
if (!r.ok) throw new Error(`${path}: ${r.status} ${await r.text()}`);
return r.json();
}
```
## Calls
### POST /v1/records
append a signed record; the answer carries its digest and inclusion proof.
```ts
const answer = await signedPost("/v1/records", record, "application/vnd.aisot.record+json");
```
### POST /v1/verify-batch
signature, inclusion proof, errata count and judge for up to 1000 digests in one call.
```ts
const answer = await (await fetch(`${AISOT}/v1/verify-batch`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({"digests":["{digest}"]}),
})).json();
```
### GET /v1/judges/{hash}
a judge version, its cohort of records, and its published prompt once retired.
```ts
const answer = await (await fetch(`${AISOT}/v1/judges/{hash}`, { headers: { accept: "application/json" } })).json();
```
## What this does not prove
- The registry stores what your judge decided. It never re-runs the judge or rescores.
OPENSPEC/CHANGES/INTEGRATE-AISOT-VERIFIED-LISTINGS/TASKS.MD
# Tasks: Verified marketplace goods
## 1. Configure
- [ ] 1.1 Set `AISOT_ORIGIN=https://aisourceoftruth.com` in the agent's environment.
- [ ] 1.2 Generate an Ed25519 key pair, register the publisher (`POST /v1/publishers`), and store `AISOT_CLIENT_ID`, `AISOT_CLIENT_SECRET`, `AISOT_KEY_ID` and the private key as secrets.
- [ ] 1.3 Mint a token at `/oauth/token` and confirm `GET /v1/me` answers with your publisher id.
- [ ] 1.4 Optional: add the MCP server `https://aisourceoftruth.com/mcp` to the agent's MCP config.
## 2. Build
- [ ] 2.1 Get the verification scope: Verification publishers are reviewed before the `publish:verification` scope is granted. Start with provenance records while you wait.
- [ ] 2.2 Publish per item: Post a verification record with the artifact, pipeline, claims, summary and the judge fields (`judge_model`, `judge_prompt_version`, `judge_prompt_hash`). (`POST /v1/records`)
- [ ] 2.3 Let buyers check: Show the digest on the listing. Buyers check it with verify-batch before paying. (`POST /v1/verify-batch`)
- [ ] 2.4 Handle a recalled judge: If a judge version is found wanting, list its cohort, re-verify, and issue errata where results change. (`GET /v1/judges/{hash}`)
## 3. Verify against the live registry
- [ ] 3.1 Get the verification scope: Your token's scope includes `publish:verification`.
- [ ] 3.2 Publish per item: The item page links its record.
- [ ] 3.3 Let buyers check: A buyer's check returns the judge version.
- [ ] 3.4 Handle a recalled judge: The cohort count matches the records you published under that judge.
## 4. Done when
- [ ] 4.1 Each listing carries a VERIFIED record a buyer can check alone.
- [ ] 4.2 A judge recall becomes a public, counted correction.
OPENSPEC/CHANGES/INTEGRATE-AISOT-VERIFIED-LISTINGS/SPECS/VERIFIED-LISTINGS/SPEC.MD
# verified-listings Specification ## ADDED Requirements ### Requirement: Verified listings The marketplace SHALL publish a verification record, with the judge pinned by hash, for every listing it labels verified. #### Scenario: Listing approved - **WHEN** a listing passes verification - **THEN** its verification record is published and its digest shown on the listing
$ curl -H 'Accept: application/json' https://aisourceoftruth.com/use-cases/verified-marketplace/openspec?lang=typescript # this page is a rendering of the API. Reproduce it as data.