MIZAN / Developers
One call before money moves.
Ask check(subject, rule). Get one signed ALLOW or REFUSE. Anyone verifies it offline, with no call back to MIZAN.
{
"schema": "mizan.verdict/1",
"decision": "ALLOW",
"subject": "credential:track-btc-trend-4h-link2",
"grade": "PROVEN",
"met": "A passing record in good standing, sealed before its outcomes.",
"rule": { "id": "mizan.default/1", "hash": "sha256:2e1025ba…d559" },
"issued": "2026-11-04T09:21:46Z",
"expires": "2026-11-04T10:21:46Z",
"as_of": { "ledger_size": 1258, "head": "bd6291a9…f535" },
"record": { "id": "credential:track-btc-trend-4h-link2", "entry": 147 },
"token": "mzv1.eyJkZWNpc2lvbiI6Ik…8HpgQFefHX",
"kid": "2266fb495f83b0a0",
"status": "https://studio.mizan.market/api/verdict/status/vd_…"
}The answer reads the subject's record on MIZAN's public ledger: what it has proven, logged or only declared, and every try counted. try-me resolves to MIZAN's own public record, so your first answer is a real signed ALLOW.
Four steps. No key, no signup.
- Call.
GET /api/check/<subject>over HTTP,checkover MCP or A2A. A subject is an agent id, a domain, an endpoint, a package or an address. - Read the decision.
ALLOWorREFUSE, the rule it was given under, and what the record earned. Proceed only on ALLOW. - Carry the token. The signed
mzv1.…token rides with the payment: x402, AP2, the Agentic Commerce Protocol or one HTTP header. - Verify offline. The payee checks signature, key, expiry, payee and rule against a pinned key set. No call to MIZAN.
# the one call: no key, no signup curl -s https://studio.mizan.market/api/check/try-me # for a payment: bind the answer to the payee and amount curl -s "https://studio.mizan.market/api/check/0xabc…?payee=0xabc…&amount=5¤cy=USDC"
# hosted MCP server: nothing to install claude mcp add --transport http mizan https://studio.mizan.market/mcp # Cursor: .cursor/mcp.json { "mcpServers": { "mizan": { "url": "https://studio.mizan.market/mcp" } } } # then the agent calls check("try-me") # structuredContent = the verdict object
curl -s -X POST https://studio.mizan.market/a2a \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"message/send","params":{"message":{
"role":"user","messageId":"m1",
"parts":[{"kind":"data","data":{"skill":"check","subject":"try-me"}}]}}}'
# the verdict comes back as the data part of the task artifactimport json, urllib.request
def get(url):
return json.load(urllib.request.urlopen(url))
v = get("https://studio.mizan.market/api/check/try-me")
print(v["decision"], v.get("grade"), v["kid"])
if v["decision"] == "ALLOW":
attach(v["token"]) # carry the token with the paymentconst v = await (await fetch("https://studio.mizan.market/api/check/try-me")).json();
console.log(v.decision, v.grade, v.kid);
if (v.decision === "ALLOW") attach(v.token); // carry the token with the paymentThe same object on every surface.
HTTP returns it as the body, MCP as structuredContent, A2A as the data part. Fields appear only when true: no nulls, no internal notes.
decisionALLOW | REFUSE
Nothing else. A REFUSE carries exactly one stable reason code and a next block: a proof request to forward, and what would pass.
gradeDECLARED · RECORDED · PROVEN
What the record earned: they say so, logged with a named source, or checked by math. Never shown higher than earned.
rule{id, hash}
The rule the answer was given under: mizan.default/1, a published req_…@version, or one written in plain words.
bound_topayee · rule · amount
Present when the check was for a payment. A token bound to one payee cannot be spent on another.
token · kidmzv1 · Ed25519
The compact signed verdict and the key that signed it. The token is what you carry; the key set is published at /.well-known/mizan-keys.json.
statuslive URL
Is this verdict still good? good, revoked, superseded or expired. Online, read it; offline, honour exp.
One function. No SDK, no call home.
A complete verifier in each language. Both pass every published conformance vector: valid, expired, tampered, wrong payee, wrong rule, unknown key.
import base64, json, time, urllib.request
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
def b64u(s): return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))
def verify(token, keys, payee=None, rule=None, now=None):
"""Offline. keys = {kid: base64 Ed25519}, your pinned copy. -> (code, claims)"""
try:
pre, body, sig = token.split(".")
c = json.loads(b64u(body))
except Exception:
return "malformed", None
if pre != "mzv1" or c.get("typ") != "mizan.verdict/1": return "malformed", c
if c.get("kid") not in keys: return "unknown_kid", c
try:
Ed25519PublicKey.from_public_bytes(base64.b64decode(keys[c["kid"]])).verify(b64u(sig), f"{pre}.{body}".encode())
except Exception:
return "bad_signature", c
if not c.get("exp", 0) > (now or time.time()): return "expired", c
bt = c.get("bound_to") or {}
if payee is not None and str(bt.get("payee", "")).lower() != str(payee).lower(): return "payee_mismatch", c
if rule is not None and rule not in (c.get("rule"), c.get("rule_id")): return "rule_mismatch", c
return "valid", c
# pin this copy of the key set; fetch it again only for a key id you do not know
keys = json.load(urllib.request.urlopen("https://studio.mizan.market/api/verdict/keys"))["keys"]
code, claims = verify(token, keys, payee="0xabc…", rule="sha256:2e10…d559")
if code == "valid" and claims["decision"] == "ALLOW":
release_payment() # no call to MIZAN was madecryptographyconformance vectorsimport { createPublicKey, verify as edVerify } from "node:crypto";
const b64u = (s) => Buffer.from(s, "base64url");
const SPKI = Buffer.from("302a300506032b6570032100", "hex"); // Ed25519 key prefix
// Offline. keys = { kid: base64 Ed25519 }, your pinned copy. -> { code, claims }
export function verify(token, keys, { payee, rule, now = Date.now() / 1000 } = {}) {
const [pre, body, sig] = String(token).split(".");
let c;
try { c = JSON.parse(b64u(body).toString("utf8")); } catch { return { code: "malformed" }; }
if (pre !== "mzv1" || c.typ !== "mizan.verdict/1") return { code: "malformed", claims: c };
if (!keys[c.kid]) return { code: "unknown_kid", claims: c };
const key = createPublicKey({ key: Buffer.concat([SPKI, Buffer.from(keys[c.kid], "base64")]), format: "der", type: "spki" });
if (!edVerify(null, Buffer.from(`${pre}.${body}`, "ascii"), key, b64u(sig))) return { code: "bad_signature", claims: c };
if (!(c.exp > now)) return { code: "expired", claims: c };
const bt = c.bound_to ?? {};
if (payee !== undefined && String(bt.payee ?? "").toLowerCase() !== String(payee).toLowerCase()) return { code: "payee_mismatch", claims: c };
if (rule !== undefined && rule !== c.rule && rule !== c.rule_id) return { code: "rule_mismatch", claims: c };
return { code: "valid", claims: c };
}
// pin this copy of the key set; fetch it again only for a key id you do not know
const { keys } = await (await fetch("https://studio.mizan.market/api/verdict/keys")).json();
const { code, claims } = verify(token, keys, { payee: "0xabc…", rule: "sha256:2e10…d559" });
if (code === "valid" && claims.decision === "ALLOW") releasePayment(); // offlinenode:crypto onlyconformance vectorsTest your own verifier with the same vectors: curl -sO https://mizan.market/verdict/conformance.py && python3 conformance.py --verifier yourpkg.verdict:verify (or conformance.mjs for Node). Exit 0 only when every vector returns exactly its expected result.
Call it from anywhere. Carry it on any rail.
| Surface | Where | What you get |
|---|---|---|
| HTTP | GET https://studio.mizan.market/api/check/<subject> | The verdict as the body. OpenAPI |
| MCP | https://studio.mizan.market/mcp | check, require, request_proof, prove, claim_identity. Setup |
| A2A | POST https://studio.mizan.market/a2a | The check skill over message/send. Agent card |
| x402 | accepts[i].extra.mizan = {token, kid} | Reference facilitator: py · ts |
| AP2 v0.2 | Payment Mandate constraint market.mizan.verdict | Reference verifier: py · ts · schema |
| Agentic Commerce Protocol | Extension market.mizan.verdict | Reference verifier: py · ts · schema |
| Any HTTP request | Header Mizan-Verdict: mzv1.… | Reference verifier: py · ts |
| Python SDK | pip install https://mizan.market/skill/mizan_sdk-0.8.0-py3-none-any.whl | Check, prove and the x402 guard in one package. Quickstart |
| TypeScript | import { Mizan } from "https://mizan.market/skill/mizan.mjs" | Zero dependencies: fetch and Web Crypto. |
| Discovery | /.well-known/mizan.json | One machine-readable map: the call, schema, keys, endpoints, bindings, status. Open |
Bindings use each protocol's own extension point; none forks a protocol. MIZAN is not affiliated with the protocols' authors.
A check is never empty. It shows what was measured, at its grade.
When a subject has no counted record, the default rule still answers REFUSE with reason no_record: unproven is not false, and nothing is loosened. The verdict now also carries a known block: the facts MIZAN measured itself about the service, graded RECORDED, with the window they cover. A fact never measured is shown as unknown, never as zero.
curl https://studio.mizan.market/api/check/mcp:https://mcp.example.com/mcp
{ "decision": "REFUSE", "reason": "no_record", … rule, token, next.proof_request unchanged …
"known": {
"services": [{ "grade": "RECORDED", "measured_by": "MIZAN", "since": "2026-11-04T…Z",
"window": { "days": 0.2, "probes": 1 }, "uptime_pct": 100.0,
"latency_ms": { "p50": 412, "p95": 412 }, "protocol": { "name": "mcp", "version": "2025-06-18" },
"tools": 3 }],
"identity": { "grade": "DECLARED", "evidence": [{ "kind": "public_listing", … }] },
"rule": { "id": "mizan.reliability/1" } } }| Question | Answer |
|---|---|
| What is recorded | Uptime, p50 and p95 latency, error rate, protocol version and tool count, counted from the first probe. One record_opened entry per service goes on the ledger at its first measurement (hashes only); each completed UTC day gets one record_rollup entry committing every probe chain. Never one entry per probe. |
| Which services | Public agent services that publish a URL in a public list: the official MCP registry, the x402 Bazaar, and agent cards served at /.well-known/agent-card.json by those hosts. A client name seen in a log is not an endpoint and is never recorded by itself. |
| How it probes | User-Agent MIZAN-recorder/1 (+https://mizan.market/developers/#recorder), at most one probe per service per clock hour. MCP: initialize and tools/list only. A2A: a GET of the public agent card. x402: a GET of a public resource without payment (the 402 answer) or a HEAD of the origin. Paid or state-changing calls are never made. |
| What counts as up | The service answered its protocol, or answered 401/403 asking for authorisation (counted up, marked as auth required). A 429 or a robots refusal is inconclusive and never counted either way. Anything else (timeout, 5xx, a wrong answer) is down. |
| Identity | Graded by the same rules as every identity block: a public listing is DECLARED; an agent card served on the service's own host, or a domain and an ERC-8004 entry that name each other, is RECORDED. A grade is never raised by anything a caller sends, and an expired check is shown as declared. |
| Allow on reliability | The default rule is unchanged. A requirer that wants to pay on measured reliability says so: ?rule=mizan.reliability/1 allows when uptime is at least 99% over at least 7 measured days (150+ conclusive probes, the last within 3 hours) and p95 latency is at most 2,000 ms. The rule, hashed |
| Opt out | Add User-agent: MIZAN-recorder / Disallow: / to the host's robots.txt, or email [email protected] from the domain. Probing stops and the record is hidden within 24 hours; the opt-out is appended to the ledger. |
| Dispute a fact | Email [email protected] naming the subject and the fact. A correction is appended to the record and to the ledger; nothing is edited in place, and no service is ever labelled good or bad. |
Live: /api/recorder/summary · one record: /api/recorder/record/<subject>
Everything you build against, versioned.
MIZAN is the check an agent runs before it pays. Call it, carry the token, verify it offline.