Skip to main content
Recover a domain Persona
Recovery lets the current domain administrator replace a Persona operator when the old key is lost or the old operator refuses to transfer it. It works for an existing domain Persona, including an expired or revoked one. Single-label Personas cannot use this procedure.
Recovery requires runtime spec 124 or later and the new Directory release. These changes are staged and have not yet been deployed to alpha. Email proof also requires the Directory operator to configure delivery to the fixed recovery mailbox.
Private keys remain local. The Directory verifies DNS and email, prepares the canonical SCALE payload, and submits the locally signed request. The chain independently checks attestor admission, proof signatures, replacement-DID signatures, proof expiry, and recovery timing.

Recovery conditions

The seven-day window begins when the first verified proof is included in finalized chain state. A controller cancellation records an objection and cannot restart the window or prevent an eligible recovery. Time alone never establishes authority. DNS and email proof challenges expire after 48 hours. Refresh proof near day seven for a recovery using one proof. A pending chain request expires after 14 days; refreshing proof does not restart its original window. Pending requests are bounded per domain, and expired chain entries are pruned when a new request is submitted. The Directory periodically purges expired local challenges. DNS and email both depend on domain administration. Changing DNS MX records can redirect recovery email, so these are not independent factors against compromise of DNS administration.

1. Create the challenge

The replacement DID must already be active and publish its Ed25519 signing keys. Request initial controllers and choose what happens to the policy:
Send that body to:
The response includes a recovery_id as 0x followed by 64 hexadecimal characters, the exact DNS record_name and record_value, expires_at in Unix milliseconds, and the fixed recovery email address. It may include the issuing directory_url; keep using that Directory for challenge verification and submission. Publish the exact TXT value at the returned record name. Up to eight distinct Ed25519 controller keys are permitted; the threshold remains 1.

Preserve or clear the policy

policy defaults to preserve. Recovery retains graph rules, references, policy IDs, and revisions, and assigns their operator metadata to the replacement DID. References to the old DID inside graphs are not rewritten. The preserved policy is the policy stored when recovery executes. policy: "clear" atomically removes the Persona’s V2 and V3 policies while replacing the operator. Persona delivery constraints remain unchanged in both modes. The choice and new controllers are bound to the signed recovery request.

2. Optionally verify email

Request delivery only to openpayload-recovery@{persona}:
The address cannot be overridden. The response reports delivery acceptance and expiry; it never contains the confirmation token. Confirm the token supplied by the email:
Tokens are single-use and expire with the 48-hour challenge. Failed confirmation attempts are limited. Email delivery can be unavailable while DNS-only recovery remains available.

3. Prepare, sign, and submit

Send an unsigned intent:
The Directory derives verified proof factors; clients cannot claim unverified factors. use_dns: false permits an email-only request. verification_days accepts 1–365 and defaults to 365; verification_expires_at can supply an explicit absolute expiry instead. The Directory fills in the current operator nonce and a short authorization expiration when omitted. The response contains canonical_payload and the complete normalized request. Decode the hexadecimal payload and sign its exact bytes locally. Preserve the returned request fields, add canonical_payload and a Base64 or hexadecimal signature, then submit:
202 Accepted returns tx_id; it means submission for processing. Wait for finalized recovery state. Invalid signatures, unverified factors, stale nonces, modified canonical payloads, or expired challenges are rejected before submission.

4. Check, refresh, or finalize

A finalized pending record includes its replacement DID, controllers, policy choice, start and expiration times, proof expirations, and cancelled. The response supplies ready_at, eligible, and proof_refresh_required. A local challenge without a chain request reports awaiting_proof. To refresh an existing pending request, create a challenge with the same replacement DID, controllers, and policy choice:
Publish or confirm the new value, prepare action prove_recovery, sign, and submit through the same prepare and submission routes. This refreshes proof without changing started_at. Prepare action finalize_recovery with the replacement DID and a recovery-controller signing key when eligible is true. Sign and submit it through the same routes. The finalized Persona becomes active under the new operator, and pending recovery requests for that Persona are removed.

Controller cancellation

A current Persona controller can prepare action cancel_recovery, using the current operator DID and its authorized signing key. Sign and submit through the same routes. Cancellation records an objection; an eligible recovery follows the conditions above. Recovery replaces Persona ownership. It does not restore a DID private key or transfer separately registered Applications.

CLI

The OpenPayload openpayload_register.py tool supports registration, renewal, DNS hooks for scheduled jobs, saved progress, and recovery. Use persona recover --policy preserve or --policy clear; the default is preserve. See the tool instructions.

Straight to the point