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.
Publishing a new version
Section titled “Publishing a new version”
One manifest lists every published account and the anchor. Re-signing is what moves an edit from the server into something anyone can check.
The sign button at the top of the dashboard is what does it, and it names the state you are in rather than making you work it out: Sign & publish changes next to an Unpublished changes badge while something is waiting, Sign / update manifest when nothing is, and Unlock your key to sign when the key is locked.
The badge is a button. Click Unpublished changes and you get the list of what publishing would actually change, compared against the manifest you have already signed: accounts added, removed, reordered or edited, disavowals in either direction, your display name, your anchor, whether the profile is public, the completeness claim, and an Emergency Kit being published, replaced or removed.

It is read-only, and deliberately has no publish button. Deciding what to sign and signing it are separate acts, because a signature is public, permanent and yours.
An account row is worth reading closely there. If a published account id would be dropped, the list says so in those words rather than calling it an edit, because that is the difference between a routine change and the one move somebody makes after taking a handle over. See Verifying an account.
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.
The banner is also a way in. See what would change on it opens What publishing would change, the same read-only list of pending changes the dashboard’s badge opens, built from the same rules. It is a reading aid and it never refuses anything: the point is that the only way to learn what a publish would do used to be publishing and comparing afterwards, which is the wrong order for an irreversible, public, signed act.
A second mechanism sits behind it, and this one the app has and the dashboard does not. If the version about to be signed would drop something the last one carried, the app asks first and names what is going. That one is a safety gate rather than a preview, which is why it is not folded into the list above: merging them would give you either a gate that nags about ordinary edits or a preview that stays quiet about the one change worth stopping for. 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.

Report and delete sit apart from each other in the Data section, with delete last, because a misclick between flagging someone else and destroying your own identity is not recoverable.
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 |
visibility | Whether the profile is public or hidden. Absent means public |
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.