For the complete documentation index, see llms.txt. The full corpus is available at llms-full.txt. This page is also available as Markdown by appending
.mdto the URL.
The signed manifest
The manifest is your identity. It is a compact JWS, signed by your key, whose payload lists your accounts, your disavowals, your anchor, your position in your own history, and the recovery policy in force. Our database is a cache of it. Everything we serve could be rebuilt from the signed file, and nothing we hold outranks it.
What the file looks like
Section titled “What the file looks like”{ "$schema": "https://realhandles.com/schema/realhandles-v1.json", "jws": "eyJ...", // authoritative: compact JWS (EdDSA), payload = the manifest "manifest": { ... }, // human-readable mirror, never trusted on its own "publicKeyJwk": { ... }, // convenience copy of the signing public key "keyId": "base64url(SHA-256(pubkey))"}Only jws counts. The manifest field is there so a person can read the file without decoding anything, and a verifier that finds the two disagreeing ignores the mirror and believes the signature. That is why verification re-parses the manifest from the bytes the signature covered, rather than from the copy sitting next to it.
The question verification asks
Section titled “The question verification asks”Is this signed by the key I already trust?
Never “does a file exist at this domain”, never “did the API return 200”, never “is this person logged in”. Those questions all have answers that somebody else can arrange. A signature has exactly one producer.
The keyId is base64url(SHA-256(...)) of the public key, canonicalized to {crv, kty, x} so it comes out the same on every platform. That fingerprint is what a verifier pins when it decides to trust you.
The server never signs
Section titled “The server never signs”The server does exactly three things with a manifest: verify it, decide whether the key that signed it is allowed to, and store it.
It holds no private key, so it has no way to produce a manifest on your behalf even if it wanted to. That is what makes “the key is the root of trust” a fact about the system rather than a promise about our conduct.
What the server checks before storing
Section titled “What the server checks before storing”- The signature verifies, and the
keyIdmatches the embedded public key. - The chain position is right. Its
seqandprevmust continue from the head we actually hold. Get this wrong and you are told to reload and sign again, which is the normal outcome when two devices publish close together. Nothing is lost. - The signing key is allowed. Either it is the key already registered to you, or the manifest carries an authorized rotation or recovery.
A login gets you nowhere here. It decides which identity row you may write to, and then the signature decides whether the write is legitimate.
Why your edit is not public until you re-sign
Section titled “Why your edit is not public until you re-sign”This is the single most confusing thing about the model, so here it is plainly.
Adding an account, verifying a domain, writing a disavowal, or reordering a list changes our copy immediately. Your profile on realhandles.com shows it right away. Your signed proof does not contain it, because nothing has signed it yet.
Until you publish and your key signs the new version:
- Anyone verifying you independently sees the previous version, which is the correct answer to give.
- The change exists only as our word for it, which is exactly the kind of claim this product exists to stop people from having to accept.
- The dashboard says so, and the mobile app says so, because the alternative would be showing you a proof that is not one.
The fix is not a background job. It is a signature, and only you can produce it. Publish.

One manifest lists every published account and the anchor. Re-signing is what moves an edit from the server into something anyone can check.
What is inside the payload
Section titled “What is inside the payload”| Field | Purpose |
|---|---|
subject | Handle, display name, public key, keyId |
accounts | Verified and claimed accounts, in the order you set |
disavowed | Signed “not me” statements |
anchor | One rotatable pointer to a hosted copy. Not the identity |
seq, prev | Position in your history and the hash of the version before it |
rotation | Present only on a version that changes the signing key |
recovery | Keys authorized in advance to take the identity back |
issued, statement | When, and the human-readable claim |
seq and prev are optional for backward compatibility: a manifest without them is treated as the first version. Everything else about the format is additive by design, so an old verifier does not break when a new field appears.
Verifying one yourself
Section titled “Verifying one yourself”The reference verifier is published as @realhandles/verify and is the exact code this site runs, extracted verbatim. The spec, JSON Schema, and conformance vectors, including invalid ones that must fail, live in realhandles/spec.

The page above runs the same check in your browser. It fetches the manifest and tests the signature against the key in the file, so a wrong answer from this server would be visible rather than persuasive.
Neither is required. It is a standard EdDSA JWS, so any JOSE library reaches the same conclusion, which is the entire point of choosing a boring format.
In the Android app
Section titled “In the Android app”The app signs manifests. The rule that the server never signs is unchanged by that, and so is the rule that only your key can produce a version: the app has to hold the key and have it unlocked, and it says which of those is missing rather than offering a publish button that could only fail.
The Identity tab carries the same split this page describes. A banner reads Saved, but not published yet while your changes are on realhandles.com and not yet in anything signed. Publish & re-sign is what closes it: your key signs the current state and a new entry joins your history.
One thing the app does that the dashboard does not. If the version about to be signed would drop something the last one carried, it asks first and names what is going. Publishing cannot be undone, and the correct moment to notice a missing account is before the signature, not after.
Two devices publishing close together produce the ordinary chain-position rejection described above. The app answers it the same way: reload and sign again. Nothing is lost.