X-EGO Human Proof
An MCP server that lets AI agents verify there is a real, present human on the other end — using passkey biometrics and short-lived, EdDSA-signed proofs. No accounts, no personal data, no cross-service tracking.
What it is.
X-EGO Human Proof exposes the X-EGO proof-of-human verification as tools for AI agents over the Model Context Protocol (MCP). It runs as a Cloudflare Worker and gives any MCP-capable agent three tools: request a verification link for a user, verify the signed proof the user brings back, and check whether a returning user has been seen before.
The human side of the flow is a Planetary ID — a digital identity bound
to a person by their passkey (fingerprint or Face ID, WebAuthn). The human verifies
on x-ego.com/verify; their biometrics never leave their device.
What it proves: a person with a registered passkey, who paid for their ID, approved this verification — now, for your service specifically.
What it doesn't prove: global uniqueness. Sybil resistance is cost-bounded: one identity, one payment.
How the pieces fit.
AI agent ──MCP──> xego-mcp-server ──HTTPS──> X-EGO API
(Claude…) (Cloudflare Worker) /api/verify
/.well-known/jwks.json
The MCP server does not verify anything cryptographically "from scratch" —
it fetches the public signing keys from the X-EGO API's JWKS endpoint and
verifies token signatures against them. The registry of seen users
(seen_pairwise) lives in the Durable Object's built-in SQL storage,
scoped to that MCP server instance.
Transport is streamable HTTP on /mcp. A health check is available at
the worker root (/ or /health).
Three tools, one loop.
1 · xego_request_proof_url
Returns the URL the agent sends a human user to, so they can prove they are human. The user verifies on the page with their passkey and receives a short-lived signed token (JWT), which they hand back to the agent. This tool performs no verification itself — it only prepares the link. No personal data is transferred.
| Argument | Description |
|---|---|
| audience | Required. The domain of the service requesting verification (e.g. forum.example.com). A URL is accepted too — it is always normalized to a bare lowercase hostname (max 253 chars). It determines who the proof will be valid for. |
Returns: verification_url (link for the user),
audience (the normalized hostname — pass this exact value as
expected_audience to xego_verify_proof), and
instructions. A non-normalizable audience returns
invalid_audience.
2 · xego_verify_proof
Verifies the token (JWT) the user brought back. It cryptographically checks the Ed25519 signature against the X-EGO public keys, validates the claims, the expiry, and the audience. A valid result means: there is a verified human on the other end who holds the passkey.
| Argument | Description |
|---|---|
| token | Required. The JWT obtained by the user after X-EGO verification. Three dot-separated parts. |
| expected_audience | Required. Your own service's domain the token must have been issued for. Domain or URL — normalized to a bare hostname exactly like in xego_request_proof_url. A token issued for a different audience is rejected (wrong_audience). This prevents token reuse across services and preserves pairwise isolation. |
| mark_as_seen | Optional, default false. If true and the token is valid, the pairwise ID is recorded as seen for that audience — useful to immediately guard against repeat use. |
Returns on success: valid: true,
pairwise_id (anonymous ID, stable per user-and-audience),
audience, expires_at (Unix time), and
human_verified: true. On failure: valid: false with a
machine-readable code and a human-readable reason
(see error codes below).
Proofs are single-use. Each token passes verification exactly once.
A second attempt to verify the same token returns token_replayed.
The jti registry is a global singleton Durable Object with an atomic
check-and-mark, so there is no window for a concurrent replay.
3 · xego_check_pairwise_seen_before
Checks whether a given pairwise ID has already been seen within an audience.
It protects against one person acting as multiple different users — multiple
accounts, repeat voting, and similar. Only the pair
(audience, pairwise_id) and the time of first occurrence are recorded.
No personal data.
| Argument | Description |
|---|---|
| audience | Required. The service domain being asked about. |
| pairwise_id | Required. The user's pairwise identifier (from the xego_verify_proof result). |
| record_if_new | Optional, default false. If true and the user has not been seen yet, they are recorded immediately — an atomic "is new? then mark" in one call. |
Returns: seen_before (boolean),
first_seen (Unix time or null), recorded_now (boolean).
Note: this registry is bound to the MCP server instance — it is per-service
protection, not a global registry.
From request to proof.
- Agent calls
xego_request_proof_urlwith its service domain asaudienceand receives a verification URL. - Agent sends the URL to the human user.
- User opens
x-ego.com/verify?audience=…, sees exactly which service the proof is being issued for, and approves with their passkey (fingerprint / Face ID). The biometric check happens entirely on their device — WebAuthn with user verification required. - X-EGO API validates the passkey assertion (full W3C WebAuthn §7.2 checks: origin, challenge, rpIdHash, UP+UV flags, signature counter), derives the pairwise ID, and signs a short-lived JWT.
- User copies the token and hands it to the agent. The page shows a live countdown of the token's validity.
- Agent calls
xego_verify_proofwith the token and itsexpected_audience. Optionally it callsxego_check_pairwise_seen_beforeto detect returning users.
Anatomy of a proof.
Proofs are JWTs signed with EdDSA (Ed25519). The verifier accepts
exclusively alg: EdDSA — a defense against algorithm-confusion and
none attacks. Claims:
| Claim | Meaning |
|---|---|
| iss | The X-EGO issuer. |
| aud | The audience — the bare lowercase hostname of the service the proof was issued for. |
| sub | The pairwise ID: xego_ + HMAC(secret, owner | audience). Stable per user-and-audience, different for every audience. |
| origin | The verified WebAuthn origin — taken from the validated client data, never from the request body. |
| iat / exp | Issued-at and expiry. Tokens live 180 seconds. Verification allows 30 s of clock skew. |
| jti | Unique token ID — the basis of single-use replay protection. |
Public keys are published at the JWKS endpoint
(kty: OKP, crv: Ed25519). The verifier caches JWKS for 5 minutes and
force-refreshes on an unknown kid, so signing keys can rotate with a
grace period without breaking verification.
Anonymous by construction.
Each service sees only a pairwise identifier — deterministically derived from the user and the audience, different for every audience. Two services can never link their users to each other, and no service ever learns the user's Planetary ID number, name, or any personal data — X-EGO stores none of it in the first place.
- Biometrics are processed on-device only (WebAuthn); nothing biometric is ever transmitted.
- The X-EGO database stores only an ID number and an irreversible hash of the passkey credential.
- The seen-users registry records only
(audience, pairwise_id, first_seen).
When verification says no.
| Code | Meaning |
|---|---|
| invalid_audience | The audience could not be normalized to a domain. |
| malformed_token | The token is not a well-formed JWT. |
| unsupported_algorithm | The token is not signed with EdDSA. |
| unknown_key | No JWKS key matches the token's kid (the kid is included so a key rotation can be told apart from a corrupt token). |
| bad_signature | The Ed25519 signature does not verify. |
| expired | The token is past its 180-second lifetime. |
| wrong_issuer | The iss claim is not X-EGO. |
| wrong_audience | The token was issued for a different audience than expected_audience. |
| missing_claims | A required claim (sub, aud, exp, jti) is absent. |
| token_replayed | This token has already been verified once. Proofs are single-use — the user must verify again. |
| jwks_unavailable | The public keys could not be fetched from the JWKS endpoint. |
What it costs.
For humans: a Planetary ID costs €3 once and includes unlimited self-verification, forever.
For services: verifying proofs is free up to 500 verifications per audience per month. Need more? Higher-volume tiers are available — get in touch. Quotas are counted per audience (the domain proofs are issued for), never per human.
Connect an agent.
Add the MCP endpoint as a connector in any MCP-capable client. In Claude: Settings → Connectors → Add custom connector, then paste the endpoint URL:
https://xego-mcp-server.bizarcoin.workers.dev/mcp
The three tools appear automatically. A minimal integration is three calls:
1. xego_request_proof_url({ audience: "your-domain.com" })
2. — user verifies at the returned URL and brings back a token —
3. xego_verify_proof({ token, expected_audience: "your-domain.com",
mark_as_seen: true })