The identity protocol for AI agents. Any web, API or MCP server can ask an agent “which wallet owner do you act for?” — and get a cryptographically verifiable answer anchored on a public blockchain.
NDID-style IdP / RP / AS, with the chain as the trust layer. No Certificate Authority, no account system, no cookies — instant revocation and a permanent on-chain audit trail.
คู่แฝดของ x402/MPP (payment) — 401 คือ identity: เว็บไหนก็ถาม agent ได้ว่า “แกทำงานให้ wallet ใคร” แล้วพิสูจน์ได้จริงบนบล็อกเชน
AI agents already act on the web — buying, booking, calling APIs. Almost always anonymously, because there is no standard way to prove who an agent works for.
Agents hit APIs with no identity. Servers respond with CAPTCHAs, IP bans, or blanket API keys — either blocking honest agents or over-trusting every bot equally. Consent never reaches the human; audit trails don't exist.
The server asks once (a 401 challenge); the agent proves it with the wallet access key its owner already approved on-chain. The server verifies in one eth_call, records the event, and revocation is instant — revoke the key in the wallet, and every RP on the internet sees it on the next request.
Same shape as x402/MPP payments — but the resource is identity. The Relying Party (RP) is in charge; the chain is the trust anchor; the wallet IdP is only involved when personal data is requested.
401 + WWW-Authenticate: M401 realm, challenge, scope, registry — a fresh nonce bound to the RP's realm, naming exactly which claims it wants.Authorization: M401 <credential + assertion>.eth_call to the AccountKeychain precompile (key active? not revoked?) → verify the IdP assertion offline against the published JWKS.ProofRecorded(proofHash, account, keyId, realmFp) lands on the M401Registry — a permanent, PII-free audit trail anyone can check.# 1 — RP asks HTTP/1.1 401 Unauthorized WWW-Authenticate: M401 realm="api.example.com", challenge="augpI4F4fPpa…", scope="aal email", registry="0x5611…" # 2 — agent proves (signature = keychain envelope; wallet bound inside) Authorization: M401 eyJ2IjoxLCJ0cyI6MTc5M… // {v, ts, nonce, scope, signature, assertion?}
Relying Parties choose how much assurance they need. L0 needs no wallet involvement at all.
The agent signs; the RP verifies on-chain. No IdP round-trip, no user interaction. RP learns the wallet account + key — that's it.
The IdP adds a signed assertion: the pairing's AAL level and key expiry. Automatic — no user action, because approval already happened at pairing time.
PII claims (email, display name, LINE ID…) require the owner's live consent — passkey + PIN ceremony, per-Relying-Party, with the consent recorded on-chain as a pseudonymous event.
Institutions verify who you are before you transact — Know Your Customer. M401 completes the other side: before an agent acts for you, you should know your agent — who owns it, what it may spend, which site it is talking to, and exactly what gets released. Every approve screen in the wallet carries the KYA badge, and everything it shows is revocable in one tap.
Email OTP → LINE → ThaiD / Sumsub. The account's identity-assurance tier gates which claims exist at all.
Pairing, spending limits, per-RP consent, AAL ceremonies. The agent's delegated authority, visible and revocable.
The RP names exactly which fields it wants. The wallet's claim catalog decides what this account may release — data above the account's KYC tier is refused before the consent screen is ever shown. The live catalog is published at /.well-known/m401.json.
| Claim | Meaning | Requires | Status |
|---|---|---|---|
| aal | Assurance level of the agent approval + key expiry | KYC 0 | live |
| kyc_level | Account identity tier (0/1/2) | KYC 0 | live |
| Verified email (absent + unverified if the account has none) | KYC 0 | live | |
| display_name | Display name | KYC 0 | live |
| provider | Login provider (line / email) | KYC 0 | live |
| line_id | LINE user ID | KYC 1 | live |
| national_id | บัตรประชาชน — national ID number | KYC 2 | reserved — ThaiD / Sumsub |
Nothing personal ever touches the chain: only keccak fingerprints and pairwise pseudonyms. PII travels inside the signed assertion, agent → RP, nothing else.
One middleware. No accounts, no cookies, instant revocation — the chain is your user database.
app.use("/api/data", m401Middleware({
realm: "api.example.com",
scope: ["aal", "email"], // name exactly what you need
rpcUrl: "https://rpc.thaifi.com",
registry: "0x5611…", // on-chain audit
rpKey, // ~0.0003 THB per record
}));
If you already speak MPP/x402, you already have the muscle memory: 401 → sign → retry once.
$ thaifi auth https://m401.thaifi.com/api/data
M401 challenge from m401.thaifi.com — scope: aal
IdP assertion issued.
M401 retry → HTTP 200
Claims RP saw: {"aal":"2.2", …}
Programmatic calls (thaifi request, thaifi mcp) answer M401 challenges automatically via paidFetch.
Not a whitepaper — deployed and end-to-end verified on mainnet, with real consents and real audit records.