Partially live — signed attestations are public; splitting, settlement and the on-chain registry are notRead an attestation
Cleaton

Quickstart

Fetch an attestation, verify it yourself, and branch on it — three steps, no key.

Argued in full in the whitepaper at §7.

Three steps end to end: fetch, verify, branch. The whole point of the design is that step three is cheap enough to sit inside a deployment path.

Fetch it#

terminal
curl -s https://dapp.cleaton.xyz/api/v1/pool/0xbeeff033f34c046626b8d0a041844c5d1a5409dd

No key and no account. Reads are free and unauthenticated, and the endpoints §11 names as public goods stay that way when metering starts. What is metered later is freshness — causing a new attestation to be computed — not access to one that exists.

The response#

response.json
{
  "pool":       "0xbeeff033f34c046626b8d0a041844c5d1a5409dd",
  "chainId":    4663,
  "poolKey":    "4663:0xbeeff033f34c046626b8d0a041844c5d1a5409dd",
  "symbol":     "steakUSDG/USDG",
  "protocol":   "Morpho",
  "horizon":    90,
  "confidence": "0.65",
  "issuedAt":   1787325423,
  "expiry":     1787411823,
  "expired":    false,
  "outcome":    "pending",
  "signature":  "0xd810beb6...4f201b",
  "attester":   "0xb6486340a930e21b7ee505111188e929120d9eda",
  "signed":     true,
  "sample":     false
}

That reads as: this pool is expected to hold at least 70% of its reference liquidity for ninety days, and the model puts 0.65 weight on its own call.

outcome is pending, held or broke. Every row published so far is pending, and that is the honest state rather than a bug — a ninety-day claim is answerable on day ninety and not before.

Branch on it#

allocate.ts
const a = await (await fetch(`${API}/pool/${pool}`)).json();

// 1. The claim is worth exactly what the signature is. See "Verify a signature".
if (!(await recovers(a, ATTESTER))) throw new Error("not signed by Cleaton");

// 2. expiry is about the ATTESTATION, not the pool. Default validity is 24h.
if (a.expiry < now()) throw new Error("stale — request a fresh attestation");

// 3. Silence is not approval. 84 attestations against 558 tracked pools:
//    most pools return no_attestation, and that is not a pass.
if (a.error === "no_attestation") return skip(pool);

// 4. Compare against YOUR lock, not against a generic threshold.
if (a.horizon < lockDays) return skip(pool);
if (Number(a.confidence) < 0.6) return skip(pool);

await allocate(pool, amount);

Or let an agent do it: pre_deploy_check over MCP takes the pool and your lock and returns the same comparison with the reason named.

Three mistakes to avoid#

Treating horizon and expiry as the same expiryA 90-day horizon issued once would otherwise be quoted for 90 days against conditions that changed on day two. Validity defaults to 24 hours.
Reading no_attestation as a passMost tracked pools have no horizon: scoring needs observed fee income, and most sources do not report it. Absence means nobody has looked, which is the opposite of an all-clear.
Re-parsing confidence as a number before hashingIt is a fixed two-decimal string because the signature covers it as text. Turn it into a float and back and the signature stops recovering.

Cleaton Documentation — partially live: attestations over REST, no settlement or registry yet.