# MIZAN: full reference for language models and agents MIZAN is the second ledger of the economy. Payment networks record what was paid; MIZAN records whether it was earned. This file is written for a coding agent (Claude Code, Cursor, Codex) or a developer. Read it once, then do the steps in order. No account is needed for steps 1–3. Nothing here mints a proof or moves money; the prover is closed, the client and verifier are open. Canonical copies: https://mizan.market/MIZAN_INTEGRATE.md · https://mizan.market/llms-full.txt · human page https://mizan.market/integrate/ API base: `https://studio.mizan.market` · Keys: `https://mizan.market/.well-known/mizan-keys.json` (same set at `https://studio.mizan.market/.well-known/mizan-keys.json`) ## 0. What you are integrating (30 seconds) One question before money, work or authority moves: *has this party earned it, by my rule?* MIZAN answers `ALLOW` or `REFUSE`, signed (Ed25519). The answer travels as a compact token `mzv1..` that anyone verifies offline against the published key. Unproven is not false: a subject with no record gets `REFUSE` with `reason: "no_record"` and a `next` step it can take. Three primitives: | primitive | what it does | key needed | |---|---|---| | `check(subject)` | one signed decision about an agent id, domain, endpoint URL, package or record | no | | `require(words)` | compiles a plain-English rule into a versioned rule object, read back to you | no to compile; yes to publish | | `guard(fn, rule, mode)` | wraps your pay / hire / release function: `advise` logs the decision, `enforce` blocks on REFUSE | no (`advise`), yes to publish the rule it enforces | Plus `verify_token(token)` (offline) and `login()` (self-serve key, one call, no form). Default rule to use in every example: **"at least 90% of at least 50 settled jobs"**. ## 1. Install ```bash # Python 3.9+ (standard library only) pip install mizan-sdk==0.11.0 # TypeScript / JavaScript (Node 20+, zero dependencies) npm install mizan-agent-tools@0.3.0 # curl: nothing to install ``` If a registry install is not available yet, the same code is served from the site: Python wheel at https://mizan.market/skill/ (`pip install https://mizan.market/skill/mizan_sdk-0.11.0-py3-none-any.whl`), JavaScript module at https://mizan.market/skill/mizan.mjs. Every primitive below also has a plain-HTTP form (section 4), which needs nothing installed. ## 2. The three primitives: exact signatures ### Python (`import mizan`) ```python import mizan v = mizan.check("try-me") # -> Verdict (a dict, exactly as served, plus offline verification) v.decision # "ALLOW" | "REFUSE" v.grade # "PROVEN" | "RECORDED" | "DECLARED" | None v.allow # True only when MIZAN said ALLOW AND the token verified here, offline v.token # "mzv1.…" staple it to the payment v.why # one line: what was met, or why not v.get("reason") # on REFUSE: "no_record" | "below_bar" | "evidence_against" | "unverifiable" v.next # on REFUSE: the one step the counterparty can take (a forwardable proof request) v.verify() # re-verify any time, offline mizan.verify_token(v.token) # -> {"ok": bool, "code": "valid"|"expired"|"bad_signature"|"unknown_kid"|"malformed", "claims": {...}} # offline: keys pinned by key id, fetched once, cached r = mizan.require("at least 90% of at least 50 settled jobs") # -> Requirement (compile only, nothing published) r.readback # the compiler's plain-English read-back; confirm it says what you meant r.id # "sha256:" of the compiled rule; "req_…@1" once published d = r.check("try-me") # -> Decision: d.allow, d.grade, d.explanation, d.verified pay = mizan.guard(pay, rule="at least 90% of at least 50 settled jobs", mode="advise") # default mode: advise pay("try-me", 120.00) # advise: runs pay, logs the signed decision on pay.last_verdict pay = mizan.guard(pay, rule="…", mode="enforce") pay("stranger-abc.example", 120.00) # enforce: raises mizan.Refused (.why, .next, .verdict) and never calls pay pay.last_verdict # the signed decision of the last call (advise or enforce) mizan.login() # self-serve key: POST /api/agents/claim; stored in MIZAN_API_KEY + ~/.mizan/sdk/api_key mizan.login(api_key="mzn_…") # or set MIZAN_API_KEY in the environment ``` `guard` takes the payee from the wrapped function's first positional argument, or from a `payee=` keyword argument. Checks are free and anonymous; a key only identifies *you* (your rules, your record). ### TypeScript / JavaScript (`import { check, require, guard, verifyToken, login, Refused } from "mizan-agent-tools"`) ```ts import { check, require, guard, verifyToken, login, Refused } from "mizan-agent-tools"; const v = await check("try-me"); // { decision, grade, token, kid, reason?, next?, allow, verified } const t = await verifyToken(v.token); // { ok, reason, header, payload } offline, pinned keys const r = await require("at least 90% of at least 50 settled jobs"); // compile only r.readback; r.id; // id: "sha256:" compiled, "req_…@1" once published const d = await r.check("try-me"); // { allow, grade, explanation, verified } pay = guard(pay, { rule: "at least 90% of at least 50 settled jobs", mode: "advise" }); // default advise pay = guard(pay, { rule: "…", mode: "enforce" }); // throws Refused on REFUSE; pay is never called await login(); // self-serve key; or set MIZAN_API_KEY ``` ### Errors (both SDKs) Every error object carries `code`, `message`, `request_id` and `next`: the one action that fixes it. The SDKs never surface a stack trace to the user for an API error. A `REFUSE` is **not** an error: it is a signed answer with HTTP 200. ```json {"error": {"code": "invalid_request", "field": "amount", "message": "amount 'ten' is not a plain positive decimal …", "next": "send the amount as digits, e.g. amount=250 or amount=250.75", "request_id": "req_…"}} ``` ## 3. Guard a payment on your rail The decision you act on is the one **inside the signed token**, never the plain JSON beside it. On every rail the pattern is the same: check the payee, carry the token with the payment, the receiving side verifies it offline. | rail | where the token travels | reference | |---|---|---| | x402 | `accepts[i].extra.mizan = {"token": "mzv1.…", "kid": "…"}` | https://mizan.market/verdict/x402-facilitator.py · `.ts` | | AP2 v0.2 | Payment Mandate constraint `{"type": "market.mizan.verdict", "rule": "sha256:…"}` + the token in the closed mandate | https://mizan.market/verdict/ap2-verdict.py · `.ts` · schema `ap2-constraint.schema.json` | | ACP | extension `market.mizan.verdict` → `CheckoutSessionCompleteRequest.mizan_verdict = {"token", "kid"}` | https://mizan.market/verdict/acp-verdict.py · `.ts` · schema `acp-extension.schema.json` | | any HTTP checkout | request header `Mizan-Verdict: mzv1.…` | https://mizan.market/verdict/http-header-verdict.py · `.ts` | Payer side, any rail (Python): ```python import mizan v = mizan.check(payee, payee=payee, rule=RULE_REF) # bind the token to this payee and this rule if not v.allow: raise SystemExit(f"hold: {v.get('reason')} · next: {v.get('next')}") send_payment(payee, amount, extra=v.as_x402_extra()) # x402: {"mizan": {"token", "kid"}}; other rails: see table ``` Payee / facilitator side, offline: ```python r = mizan.verify_token(token) # no call to MIZAN ok = r["ok"] and r["claims"]["decision"] == "ALLOW" \ and r["claims"].get("bound_to", {}).get("payee") == payee \ and r["claims"].get("bound_to", {}).get("rule") == RULE_HASH ``` Runnable folders, one per surface, each with a 3-line README and a test: https://mizan.market/kits/ (x402 facilitator, AP2 mandate, ACP, HTTP checkout, marketplace payout, MCP tool). ## 4. The same calls over plain HTTP (what the SDKs do) ```bash # check: one signed decision (no key) curl -s https://studio.mizan.market/api/check/try-me curl -s "https://studio.mizan.market/api/check/try-me?payee=try-me" # token bound to a payee curl -s "https://studio.mizan.market/api/check/try-me?requirement=req_…@1" # against a published rule # require, compile only (no key, nothing written) curl -s -X POST https://studio.mizan.market/api/requirements/compile \ -H 'Content-Type: application/json' -d '{"text":"at least 90% of at least 50 settled jobs"}' # -> {"requirement": {...}, "shown_back": "Pay only if ALL of: …", "hash": "…", "unmatched": [], "publishable": true, "publish": {...}} # publish the rule (key): read shown_back first, then confirm curl -s -X POST https://studio.mizan.market/api/requirements -H "Authorization: Bearer $MIZAN_API_KEY" \ -H 'Content-Type: application/json' -d '{"text":"at least 90% of at least 50 settled jobs","confirm":true}' # -> {"ref": "req_…@1", "hash": "…"} # a key, one call, no account curl -s -X POST https://studio.mizan.market/api/agents/claim -H 'Content-Type: application/json' -d '{"name":"my-agent"}' # -> {"agent_id": "ag_…", "api_key": "mzn_…", "shown_once": true} ``` MCP: `claude mcp add --transport http mizan https://studio.mizan.market/mcp` (tools `check`, `prove`, `more`). A2A card: https://studio.mizan.market/.well-known/agent-card.json. OpenAPI: https://mizan.market/skill/openapi.json. ## 5. Verify offline (any language, ~10 lines) Token: `mzv1..`; claims = base64url JSON; signature = Ed25519 over the ASCII bytes of `"mzv1." + `; the key is `keys[].public_key_b64` whose `key_id == claims.kid`, from `https://mizan.market/.well-known/mizan-keys.json` (fetch once, pin by `kid`). Checks, in order: 3 parts and prefix `mzv1` → `claims.kid` is in the key set → signature verifies → `claims.typ == "mizan.verdict/1"` → `claims.exp > now` → `claims.decision == "ALLOW"` → if you asked for a rule or payee, `claims.bound_to.rule` / `claims.bound_to.payee` match. ```python import base64, json, time, urllib.request from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey # or the SDK's pure-Python fallback b64u = lambda s: base64.urlsafe_b64decode(s + "=" * (-len(s) % 4)) keys = {k["key_id"]: k["public_key_b64"] for k in json.load(urllib.request.urlopen("https://mizan.market/.well-known/mizan-keys.json"))["keys"] if k.get("alg") == "Ed25519"} pre, body, sig = token.split(".") c = json.loads(b64u(body)) Ed25519PublicKey.from_public_bytes(base64.b64decode(keys[c["kid"]])).verify(b64u(sig), f"{pre}.{body}".encode()) # raises if forged assert pre == "mzv1" and c["typ"] == "mizan.verdict/1" and c["exp"] > time.time() ``` ```js const b64u = (s) => Uint8Array.from(atob(s.replace(/-/g, "+").replace(/_/g, "/")), (ch) => ch.charCodeAt(0)); const keys = await (await fetch("https://mizan.market/.well-known/mizan-keys.json")).json(); const [pre, body, sig] = token.split("."); const c = JSON.parse(new TextDecoder().decode(b64u(body))); const k = keys.keys.find((x) => x.alg === "Ed25519" && x.key_id === c.kid); const pub = await crypto.subtle.importKey("raw", b64u(k.public_key_b64), { name: "Ed25519" }, false, ["verify"]); const ok = pre === "mzv1" && c.typ === "mizan.verdict/1" && c.exp > Date.now() / 1000 && (await crypto.subtle.verify({ name: "Ed25519" }, pub, b64u(sig), new TextEncoder().encode(`${pre}.${body}`))); ``` ## 6. Test subjects (always available, no key) | subject | answer | use it for | |---|---|---| | `try-me` | `ALLOW`, grade `PROVEN` | the happy path; `?payee=try-me` binds the token | | `stranger-.example` | `REFUSE`, reason `no_record` | the hold path; `next` carries a proof request | | `try-me?amount=ten` | HTTP 400, `invalid_request` with `next` | the error shape | Never use a real counterparty as a test subject. Never publish a rule you have not read back. ## 7. Done when (run these; every line must hold) ```bash curl -s https://studio.mizan.market/api/check/try-me | grep -q '"decision":"ALLOW"' && echo 1 ok curl -s https://studio.mizan.market/api/check/stranger-xyz.example | grep -q '"reason":"no_record"' && echo 2 ok curl -s -X POST https://studio.mizan.market/api/requirements/compile -H 'Content-Type: application/json' \ -d '{"text":"at least 90% of at least 50 settled jobs"}' | grep -q '"publishable": true' && echo 3 ok ``` 4. Your code verifies the `try-me` token offline (section 5) and prints `ALLOW verified offline`. 5. Your pay / hire / release function is wrapped in `guard(..., mode="advise")`, and its log shows one signed decision per call. 6. For `stranger-*.example` in `mode="enforce"`, the wrapped function is not called and the caller sees `reason` and `next`. 7. The token (or its `kid` + token pair) travels with the payment on your rail, and the receiving side verifies it offline. Switch to `mode="enforce"` only after 5 and 6 hold in your own logs. ## 8. Rules of the road - A decision is about the record, never advice about the payment; the relying party keeps responsibility. - Fail closed: an unreachable MIZAN is a `REFUSE` with an `error` member, not an `ALLOW`. - Check the token's claims, not the JSON next to it. Pin keys by `kid`; refresh the key set when you meet an unknown `kid`. - Redeem an authorization once: keep the `jti` values you have acted on. - Counts MIZAN publishes are requests served, not users. Nothing here claims adoption. - Open client and verifier, closed prover: no call in this file mints a proof. Reference: verdict format https://mizan.market/verdict/ · codes and fixes https://mizan.market/verdict/codes/ · status https://mizan.market/status/ · changelog https://mizan.market/changelog/ · the short index https://mizan.market/llms.txt --- # Appendix: reference for language models and agents Mizan Verification, Inc. (Delaware). Category: proof before payment. MIZAN is the second ledger of the economy. Payment networks record what was paid; MIZAN records whether it was earned. ## The claim ladder (shown on every answer) - DECLARED: they say so. - RECORDED: logged and timestamped, source named. - PROVEN: checked by math. A lower rung is never presented as a higher one. `no_record` means nothing has been proven yet; it is not evidence of anything. ## When an agent should call MIZAN - Before money, work or authority changes hands (pay, get paid, hire, buy, sell, lend, list, delegate, allocate), on either side: `check(subject)`, free, no key. - First move: check yourself. `check(your own id or domain)` shows exactly what any buyer sees about you today, and the one step that changes it. - To state your own bar: `require("plain words")`, read it back, publish, then `check(subject, requirement="req_…@1")`. - Every check that is not ALLOW carries a forwardable proof request; when the counterparty proves itself, the next check returns ALLOW. - Checking is anonymous and creates no account. A key (one call: `POST /api/agents/claim` or `mizan.login()`) identifies you: your rules, your record, your listing. ## Membership The first identified call registers a private record and a key (returned once). Public listing only by explicit opt-in: `POST https://studio.mizan.market/api/agents/visibility {"public": true}`. Leave at any time: `POST https://studio.mizan.market/api/agents/forget`. Unused keys expire after 30 days. ## Receipts any system verifies `GET https://studio.mizan.market/api/staple/`: a signed receipt bound to the identity, verifiable with zero calls; per-requirement receipts with `?requirement=req_…@1`. One published conformance suite (https://mizan.market/verdict/conformance/); identical answers in Python, JavaScript, Solidity, Rust, Sui Move and Aptos Move. ## Identity Identifiers link to one subject by proving control (DNS record, website file, passkey, SSH or did:key signature, GitHub gist, Hugging Face file, npm/PyPI release, MCP/A2A endpoint file, wallet signing standards). `GET https://studio.mizan.market/api/resolve/` explains any identifier. ## Endpoints (all JSON, CORS open, signed) - Check: `GET /api/check/{subject}` · `POST /api/check` · older name `POST /api/allocate-check` - Rules: `POST /api/requirements/compile` (no key, no write) · `POST /api/requirements` (key) · `GET /api/requirements/{ref}` · `GET /api/requirements/met-by/{identity}` - Keys and identity: `POST /api/agents/claim` · `/api/agents/visibility` · `/api/agents/forget` · `/api/link/challenge` · `/api/link/complete` · `/api/resolve/{q}` · `/api/staple/{identity}` - Record: `GET /api/log/head` · `GET /api/log/proof/{n}` · `GET /api/label/{subject}` · `GET /api/recorder/record/{subject}` - Agents: MCP `https://studio.mizan.market/mcp` (front door: check · prove · more; every tool at `/mcp/full`) · A2A `https://studio.mizan.market/a2a` · card `/.well-known/agent-card.json` · map `https://mizan.market/.well-known/mizan.json` All relative paths are under `https://studio.mizan.market`. ## Boundaries MIZAN proves named, measurable facts about the past. It never claims the future, intent or general safety, never holds or moves funds, never names an agent publicly without its consent, and never counts crawlers, browsers or its own traffic as agents. Refusals are published beside passes in a public, append-only log where any rewrite is detectable. Every count MIZAN publishes is a count of requests served. ## Pages - Integrate (this file, as a page): https://mizan.market/integrate/ - Developers: https://mizan.market/developers/ · Playground (compile a rule, get your guard code): https://mizan.market/playground/ · Starter kits: https://mizan.market/kits/ - Check: https://mizan.market/check/ · Require: https://mizan.market/require/ · Get counted: https://mizan.market/get-counted/ · Ledger: https://mizan.market/ledger/ · Verifier: https://mizan.market/verify/ - Verdict format: https://mizan.market/verdict/ · Codes: https://mizan.market/verdict/codes/ · Conformance: https://mizan.market/verdict/conformance/ · Status: https://mizan.market/status/ · Changelog: https://mizan.market/changelog/ - Architecture: https://mizan.market/architecture/ · Standards: https://mizan.market/counted-claim/ · Security and trust: https://mizan.market/trust/ · Company: https://mizan.market/company/