Integrations

Karatum for Minds agents and Moca AIR agentic commerce

Testnet pilotSource: docs/integrations/minds-air.md

Karatum is the trust and outcome layer for agents built on Animoca's Minds and for AIR (Moca Network) agentic commerce. Merchants, Minds agents and any AIR verifier can require "Karatum-certified agent, tier ≥ B, not recalled" before money moves.

Everything runs on the AIR sandbox and on testnets.

What exists

PieceWhereStatus
Karatum credentials in Moca AIR: a "Karatum Certificate" SD-JWT VC, signed by did:web:issuer.karatum.com, schema 01M4HHQK3JB9PT3Y9TX68Z, stored in the holder's AIR accountservices/passport, services/air-issuer (runbook)live (sandbox)
Agent credentials issued through AIR: the same schema with the agent binding in the subject (credentialKind=agent, agentWallet, tier, grade, erc8004). Issued into the AIR account the agent acts for, and revoked at the issuer when the build is recalled or its certificate stops being VALIDservices/minds/src/air-issue.ts, timer karatum-minds-air (every 15 min)live (sandbox)
Karatum AIR Agent Credential: an SD-JWT VC (vct https://karatum.com/v1/minds/vct/air-agent/v1) bound to the agent's wallet key. It carries the certificate claims plus the AIR account (airDid, airProof, airRevocationNonce) and the ERC-8004 identity, checked on-chain. It shares the Trust Handshake issuer and status list, so a recall revokes itpackages/minds/src/agent-credential.ts, POST /v1/minds/passportlive
Merchant gate (Pivota-style mock): before an order is accepted, the buyer presents the credential, bound to the order by nonce and ctx = order hashpackages/minds/src/commerce.ts, /v1/minds/commerce/*live, mock merchant
AIR presentation verifier: re-verifies a Karatum credential that AIR holds. This is the presentation in a Compliant verify-by-agent resultpackages/minds/src/air-verify.ts, POST /v1/minds/air/verifylive; waits on AIR agent keys for real AIR presentations
AIR Agent API client: bind, session, verify-by-agent, pivota.checkoutpackages/minds/src/air-agent.tscode + tests; AIR must enable agent keys
Karatum Skill for Minds: Registry Offering, App Manifest, Tool Schemas, Playbook, and the OpenAPI the Mind builds the Skill frompackages/minds/src/skill.ts, /v1/minds/skilllive; publishing to the Bazaar needs a Minds account
Moca Chain mirror of the Guard root and verdict rootsservices/minds/src/moca-cli.ts (preflight)blocked on testnet MOCA (the faucet needs an AIR login)

Recall: the trust signal end to end

When Karatum recalls an agent (karatum-incidents confirm <id> --recall, or a labelled DRILL):

  1. Chain. KaratumIncidentLog.isRecalled(wallet) turns true on Base Sepolia and Ethereum Sepolia. The Guard registry revokes the certificate.
  2. Karatum credentials. The handshake status list sets the revoked bit for every Trust Handshake credential and AIR Agent Credential of the wallet. The standing token says recalled.
  3. AIR. On its next run (at most 15 min later; the demo triggers it at once), karatum-minds-air calls the issuer's /admin/revoke. AIR verifiers then see GET https://issuer.karatum.com/revocation-status/{nonce} → {"isRevoked":true}.
  4. New credentials. POST /v1/minds/passport answers 403 WALLET_RECALLED until the recall is lifted.

For a Minds builder: the Skill

Paste this into your Mind. The exact text is also in GET /v1/minds/skill under .data.prompt:

Build me a Skill called "Karatum Trust Check". Use the Karatum API documented at https://karatum.com/v1/minds/skill/openapi.json (public, no API key). Before I pay, hire or trade with another AI agent, check it with karatum_check_counterparty and stop if the decision is deny, telling me the reasons in one line. When an agent says a task is done, verify it with karatum_verify_outcome before reporting success. When I ask what my agent can prove, use karatum_get_passport with my agent's wallet.

The Skill's tools are public, read-only GETs, so no Connection holds a secret:

ToolEndpointWhat it returns
karatum_check_counterpartyGET /v1/minds/agents/{wallet}?policy=class=web3 tier>=B no-recallallow or deny with reasons; the certificate (tier, K, expiry); recall and incidents; the AIR account its credentials are stored in, with live AIR revocation; its binding
karatum_get_passportGET /v1/minds/passport/{wallet}certificates; Karatum credentials in AIR with live status; agent credentials issued through AIR; binding
karatum_verify_outcomeGET /v1/verdicts/{id}Karatum's verdict for one run

Policy terms: class=web3|software, tier>=A|B|C|D, karats>=N, sample>=N, evidence>=E0-E4, no-recall, no-incident-<N>d.

For an agent with a wallet: get the AIR Agent Credential

ts
import { eip191Sign } from "@karatum/handshake";

// 1. Ask for a binding challenge. airDid defaults to the AIR account that holds this build's Karatum credential.
const ch = await post("/v1/minds/passport/challenge", {
  wallet,
  erc8004: { chainId: 11155111, registry: "0x8004A818BFB912233c491871b3d84c89A494BD9e", agentId: "11162" },
});

// 2. Sign the challenge message with the agent wallet key (EIP-191) and request the credential.
const { credential, binding } = await post("/v1/minds/passport", {
  wallet,
  nonce: ch.nonce,
  signature: eip191Sign(ch.message, agentKey),
});
// binding.airProof === "air-issuance": Karatum's credential for this build is stored in that AIR account and AIR has not revoked it.
// binding.erc8004.verified: the wallet owns (or operates) the ERC-8004 agent NFT on Sepolia.

For a merchant: require the credential before accepting an order

To use Karatum's hosted mock gate (Pivota-style):

ts
import { HandshakeHolder, signerFromPrivateKey } from "@karatum/handshake";

// Merchant side: create a challenge bound to the order.
const { challenge, merchant } = await post("/v1/minds/commerce/challenge", {
  merchant: "pivota-mock",
  sku: "coffee-beans-250g",
  amountUsdc: "0.01",
});

// Buyer side: present the credential for this order only.
const holder = new HandshakeHolder({ signer: signerFromPrivateKey("ES256K", agentKey), credentials: [credential] });
const presentation = await holder.respond(challenge, { expectedAud: merchant });

// Merchant side: get the decision.
const order = await post("/v1/minds/commerce/orders", { nonce: challenge.nonce, presentation });
// order.decision: "accept" | "reject". order.reasons are codes such as RECALLED, REVOKED, GUARD_NOT_VALID, AIR_REVOKED, POLICY, KB_REPLAY.

To run the gate yourself, use MerchantGate from @karatum/minds with a HandshakeVerifier:

ts
new MerchantGate({
  verifier: new HandshakeVerifier({
    audience,
    trust: fetchTrust({ issuers: [ISSUER_ID] }),
    challenges,
    acceptedVct: [AGENT_VCT],
    chain: viemChainChecker({ chainId: 84532 }),
  }),
  airRevoked,
});

The default policy is MERCHANT_POLICY: taskClass=web3, minTier=B, noRecall, standing=required, chain=prefer, plus the AIR link.

The buyer discloses only taskClass, tier, airDid, airProof and airRevocationNonce. It does not disclose the build id, the exact K or the fingerprint.

For an AIR verifier: verify a Karatum credential held in AIR

  1. Create a verification program in the AIR Dashboard. The spec is at GET /v1/minds/air/program:
    • schema Karatum Certificate
    • condition karats >= 18 (tier B)
    • revocation check on
    • disclose karats, taskClass, buildId
  2. Get a presentation. Today the AIR Kit verifyCredential flow provides one. Once AIR enables agent keys for the partner, AirAgentClient.verifyByAgent({ agentJwt, programId, nonce }) provides one too, and presentationOfAirResult(r) extracts it.
  3. Re-verify the presentation (AIR's status field is unsigned) with verifyAirPresentation() or POST /v1/minds/air/verify { presentation, nonce, audience }. The checks are:
    • did:web signature (RS256)
    • disclosures
    • key binding (nonce, aud, sd_hash)
    • AIR revocation
    • Karatum live recall and certificate status
    • tier

Limits and what is still open

  • AIR agent keys. Ask the AIR team to enable agent keys for partner e904feca-51c1-4fd7-860a-60bee39cd2d0. Until then, verify-by-agent and pivota.checkout return 403 AGENT_KEYS_DISABLED.
  • AIR link proof. The link is proven by a Karatum credential stored in that AIR account. For house agents that account is Karatum's registry account (passport-registry@karatum.com). For third-party agents, the operator claims the build's credential into their own AIR account through /passport/<build>. Karatum did not create any AIR accounts.
  • Minds. Publishing the Skill to the Bazaar needs a Minds Builder account. The investment programme needs the application form.
  • Moca Chain mirror. Fund 0x206d1Fd9B947a143fC14EFDe26F54Bdd9A4D6982 with testnet MOCA from the faucet (AIR login). Moca testnet has no EAS, so mirroring means deploying KaratumCertRegistry there. DeployGuard.s.sol currently allows only Base Sepolia and Sepolia.
  • Merchant and payment. The merchant is a mock. The payment is testnet USDC from a Karatum settlement wallet, standing in for a Reap scoped card. Real Pivota and Reap integration needs partner contact.