The RealHandles protocol
An open, portable format for proving that a set of online accounts belong to one real person. Signed by the person's own key, hosted anywhere, verifiable by anyone, including software and AI agents, without trusting RealHandles.
The signed manifest
The unit of the protocol is a file, conventionally realhandles.json. The authoritative part is a compact JWS (Ed25519 / EdDSA) whose payload is the manifest; a human-readable mirror sits beside it.
{
"$schema": "https://realhandles.com/schema/realhandles-v1.json",
"jws": "eyJhbGciOiJFZERTQSIsInR5cCI6InJoLW1hbmlmZXN0Iiwia2lkIjoi..."
"manifest": {
"version": "1",
"subject": {
"username": "davidvkimball",
"displayName": "David V. Kimball",
"publicKey": { "kty": "OKP", "crv": "Ed25519", "x": "..." },
"keyId": "base64url(SHA-256(publicKey))"
},
"accounts": [
{ "platform": "github", "handle": "davidvkimball",
"profileUrl": "https://github.com/davidvkimball",
"method": "oauth", "verifiedAt": "2026-07-13T..." }
],
"anchor": { "url": "https://davidvkimball.com/.well-known/realhandles.json", "kind": "domain" },
"issued": "2026-07-13T...",
"statement": "These accounts belong to ..."
},
"publicKeyJwk": { "kty": "OKP", "crv": "Ed25519", "x": "..." },
"keyId": "..."
} A method of oauth, tweet-proof, atproto, post-proof, description-proof, or domain-control means the account was independently confirmed. self-attested means it is a signed claim, not confirmed.
Verifying it (independently)
1. Base64url-decode the jws payload to read the manifest.
2. Verify the JWS signature against manifest.subject.publicKey (EdDSA). Any off-the-shelf JOSE library does this.
3. Recompute keyId = base64url(SHA-256(canonical publicKey)) and confirm it matches.
4. Trust decision: pin that keyId the first time you see it (trust on first use). If it later changes for the same person without a signed migration, treat it as suspect. The key is the identity; the username and domain are just pointers.
This holds even if RealHandles is offline. Try it on the independent verifier.
Open source
You do not have to trust RealHandles. The wire format is a public spec with conformance vectors, and the reference verifier is on npm, the exact same code RealHandles runs. Verify any profile in three lines, from anywhere.
npm install @realhandles/verify
import { verifySignedManifest } from '@realhandles/verify';
const file = await fetch('https://realhandles.com/david/realhandles.json').then((r) => r.json());
const { valid, keyId, manifest } = await verifySignedManifest(file); Discovery
Hosted: realhandles.com/<handle>/realhandles.json is always current.
Self-hosted: /.well-known/realhandles.json on your own domain, or a public Gist. Host the full manifest, or a signed pointer file (host once; it names your canonical URL and proves you control the location).
Linked: <link rel="me" href="https://realhandles.com/<handle>"> from your homepage makes your identity discoverable and, because rel="me" is bidirectional, corroborated.
Importing profiles
RealHandles reads a person's profiles from the sources they already publish, no new format required:
rel="me" links (the IndieWeb standard, also how Mastodon verifies).
schema.org sameAs in JSON-LD ({"@type":"Person","sameAs":[...]}).
A JSON list of links, e.g. a /api/social.json that returns [{ "name": "...", "url": "..." }].
Imported accounts start as Claimed and can be upgraded to Verified per platform.
For AI agents
RealHandles is designed to be machine-readable, so an agent can gather and structure a person's online identity reliably.
Structured read API: GET /api/identity/<handle> returns a machine-readable summary (keyId, did:key, trust score, accounts split verified vs claimed) plus the signed jws, so you can verify it yourself.
MCP server: add @realhandles/mcp to any MCP client (npx @realhandles/mcp) for verify_identity and check_link tools. It verifies each proof locally with @realhandles/verify, so results never depend on trusting our API.
To read someone's real accounts: fetch realhandles.com/<handle>/realhandles.json, verify the signature, and read manifest.accounts. Trust the ones whose method is not self-attested.
To structure a messy web presence: parse the person's rel="me" links and sameAs data, or read their /api/social.json. Map each URL to a platform by its host.
For DID interop: the subject's public key is also a standard did:key, usable with the wider verifiable-credentials ecosystem.
The /social convention
In the spirit of /now and /uses pages: publish a /social page (for people) plus a /api/social.json (for machines) listing your accounts, then get them verified and listed here. One human-friendly page, one machine-friendly list, and a cryptographic proof underneath.