> For the complete documentation index, see [llms.txt](https://docs.t-rex.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.t-rex.network/guides/identity/28.-install-the-recovery-module-and-manage-guardians.md).

# 28. Install the recovery module and manage guardians

Installing the recovery module gives an identity a way back when its `MANAGEMENT` keys are lost. The Identity Owner installs the `RecoveryModule` with the guardians it chose, a threshold, a delay and an expiration, and gives the module the authority to change keys. It then keeps the guardian set current. All of this takes a `MANAGEMENT` key, so it is done while the owner still has its keys, ideally in the same session as the creation of the identity.

## At a glance

|                   |                                                                                                                                                                                                                                                                                                                                                    |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Triggered by**  | Identity Owner                                                                                                                                                                                                                                                                                                                                     |
| **Actors**        | Identity Owner, Guardian, Platform                                                                                                                                                                                                                                                                                                                 |
| **Contracts**     | ONCHAINID, `RecoveryModule`, `ERC734Validator`                                                                                                                                                                                                                                                                                                     |
| **Preconditions** | The identity exists (see [02](/guides/identity/02.-deploy-an-onchainid.md)); the owner controls a `MANAGEMENT` key of the identity (see [22](/guides/identity/22.-add-and-remove-keys-on-an-onchainid.md)); the address of the `RecoveryModule` is known: one module serves every identity; each guardian has agreed and has given its signer data |
| **Result**        | The module is installed on the identity with its guardians, weights, threshold, delay and expiration, and holds the `MANAGEMENT` purpose                                                                                                                                                                                                           |

## Actors and roles

| Actor          | Role in this flow                                                                                  | What they hold                              |
| -------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------- |
| Identity Owner | Chooses the guardians and the settings, installs the module and maintains it.                      | A `MANAGEMENT` key of the identity          |
| Guardian       | Agrees to act for the owner and provides the signer it signs with. Does not transact in this flow. | A signer of its own. No key of the identity |
| Platform       | Builds the installation data, prepares the calls and shows the settings back to the owner.         | Whatever keys the owner has authorized      |

## Sequence

```mermaid
sequenceDiagram
    participant Owner as Identity Owner
    participant Platform
    participant Identity as ONCHAINID
    participant Recovery as RecoveryModule
    participant Validator as ERC734Validator

    Owner->>Platform: guardians, weights, threshold, delay, expiration
    Platform->>Owner: installation data, to submit
    Owner->>Identity: installModule(2, recoveryModule, initData)
    Identity->>Identity: caller holds MANAGEMENT, module not installed
    Identity->>Recovery: onInstall(initData)
    Recovery->>Recovery: guardians valid, threshold reachable
    Owner->>Identity: addKeyWithData(keyHash, MANAGEMENT, MODULE, signerData, clientData)
    Identity->>Validator: register the module as a key
    Validator-->>Platform: KeyAdded
    Platform->>Recovery: guardians, threshold, delay, expiration of the identity (read)
    opt later, to change the guardians or the settings
        Owner->>Identity: execute(identity, 0, call to the module)
        Identity->>Recovery: addGuardians, removeGuardians, setThreshold, setDelay or setExpiration
    end
```

## Step by step

### Step 1. Choose the guardians and the settings

**Who:** Identity Owner, with the Platform

**Call:** Off-chain

**Before the call:** The owner has decided who its guardians are, and each guardian has given the signer it signs with.

**What happens:** The owner fixes five settings.

| Setting    | Meaning                                                                                                                                       |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| Guardians  | The signers that can authorize a recovery together. Each is given as signer data: for an address, its 20 bytes.                               |
| Weights    | The weight of each guardian's signature, one for each guardian and at least 1. One each, unless the owner wants a guardian to count for more. |
| Threshold  | The total weight a recovery or a cancellation by guardians must reach. For example, 2 with three guardians of weight 1.                       |
| Delay      | The time between the scheduling of a recovery and the moment it can execute. The owner can cancel during this time.                           |
| Expiration | The time a recovery stays executable after its delay. Past it, the recovery has to be scheduled again.                                        |

The Platform encodes them as the installation data, `initData`: the two-byte length of the executor arguments, the executor arguments `abi.encodePacked(uint32 delay, uint32 expiration)`, the two-byte length of the guardian arguments, and the guardian arguments `abi.encode(bytes[] guardians, uint64 threshold, uint64[] weights)`.

**What changes:** Nothing on-chain.

### Step 2. Install the module

**Who:** Identity Owner (`MANAGEMENT` key)

**Call:** `Identity.installModule(2, recoveryModule, initData)`, where 2 is the ERC-7579 module type of an executor

**Before the call:** Step 1 is done. The Platform shows the owner the guardians and the settings it is about to install.

**What the contract checks:**

1. The caller holds `MANAGEMENT` on the identity, or is the identity itself. Otherwise the call reverts with `SenderDoesNotHaveManagementKey`.
2. The module declares itself an executor. Otherwise `ERC7579MismatchedModuleTypeId`.
3. The module is not already installed on the identity. Otherwise `ERC7579AlreadyInstalledModule`.
4. In the module, the identity has no settings yet. Otherwise `ERC7579DelayedExecutorAlreadyInstalled` or `ERC7579MultisigAlreadyInstalled`.
5. The guardian arguments decode and list at least one guardian. Otherwise `ERC7579MultisigInvalidInitData` or `ERC7579MultisigEmptySigners`.
6. Every guardian is at least 20 bytes long and is listed once. Otherwise `ERC7579MultisigInvalidSigner` or `ERC7579MultisigAlreadyExists`.
7. There is one weight for each guardian, and no weight is zero. Otherwise `ERC7579MultisigMismatchedLength` or `ERC7579MultisigInvalidWeight`.
8. The threshold is above zero and within the total weight of the guardians. Otherwise `ERC7579MultisigZeroThreshold` or `ERC7579MultisigUnreachableThreshold`.

**What changes:** The identity records the module as one of its executors and emits `ModuleInstalled`, then `ModuleInstallData(moduleTypeId, module, initData, caller)`. The module stores the guardians, the weights, the threshold, the delay and the expiration for this identity, and emits `ERC7913SignerAdded` for each guardian, `ERC7579MultisigWeightChanged` for each weight above 1, `ERC7913ThresholdSet`, `ERC7579ExecutorDelayUpdated` and `ERC7579ExecutorExpirationUpdated`. The module cannot change a key yet.

### Step 3. Give the module the authority to change keys

**Who:** Identity Owner (`MANAGEMENT` key)

**Call:** `Identity.addKeyWithData(keyHash, 1, 4, signerData, clientData)`, with `signerData` the 20 bytes of the module's address, `keyHash = keccak256(signerData)`, purpose 1 for `MANAGEMENT`, key type 4 for `MODULE` and an empty `clientData`

**Before the call:** Step 2 is confirmed.

**What the contract checks:** As for any key, in [22](/guides/identity/22.-add-and-remove-keys-on-an-onchainid.md): the caller holds `MANAGEMENT`, the key hash matches the signer data, and the module does not already hold the purpose. Otherwise `SenderDoesNotHaveManagementKey`, `InvalidSignerData` or `KeyAlreadyRegistered`.

**What changes:** The key registry records the module as a key of type `MODULE` with the purpose `MANAGEMENT`, and emits `KeyAdded(identity, keyHash, purpose, keyType, signerData, clientData, caller)`. A recovery executed by the module is now allowed to add a `MANAGEMENT` key to the identity. A key of type `MODULE` cannot sign, and it does not count as a manager when the identity checks that a `MANAGEMENT` signer remains.

### Step 4. Confirm the settings

**Who:** Platform

**Call:** Off-chain, using read calls

**Before the call:** Steps 2 and 3 are confirmed.

**What happens:** The Platform reads `Identity.isModuleInstalled(2, recoveryModule, "")` and `Identity.keyHasPurpose(keyHash, 1)`, which must both be true. On the module, it reads `getGuardians(identity, start, end)`, `getGuardianCount(identity)`, `signerWeight(identity, guardian)` for each guardian, `totalWeight(identity)`, `threshold(identity)`, `getDelay(identity)` and `getExpiration(identity)`, and shows them to the owner. The owner tells each guardian that it is now a guardian of this identity, and how a request for recovery reaches it.

**What changes:** Nothing on-chain.

### Step 5. Change the guardians or the settings

Later, each time a guardian has to be replaced or a setting adjusted.

**Who:** Identity Owner (`MANAGEMENT` key)

**Call:** `Identity.execute(identity, 0, data)`. `data` is a call of the identity to its own ERC-7579 `execute(mode, executionCalldata)`, and `executionCalldata` targets the module with one of: `addGuardians(guardians)`, `removeGuardians(guardians)`, `setSignerWeights(guardians, weights)`, `setThreshold(threshold)`, `setDelay(delay)`, `setExpiration(expiration)`

**Before the call:** The owner has decided the change. For a new guardian, the guardian has agreed and given its signer data. The Platform reads the current settings, as in step 4.

**What the contract checks:**

1. The request comes from a `MANAGEMENT` key. The identity's execution queue then runs it at once: the identity calls itself, and the module sees the identity as its caller. A request queued by a key that does not hold `MANAGEMENT` is recorded and not run.
2. The module changes the settings of its caller, and of no one else. Only a call that comes from the identity changes the identity's settings. The identity refuses to dispatch a call into one of its own modules unless it is calling itself: `OwnModuleTargetBlocked`.
3. In the module: a guardian to add is at least 20 bytes long and not already listed (`ERC7579MultisigInvalidSigner`, `ERC7579MultisigAlreadyExists`). A guardian to remove or to reweight is listed (`ERC7579MultisigNonexistentSigner`). A weight is not zero (`ERC7579MultisigInvalidWeight`). After the change, the threshold is above zero and within the total weight (`ERC7579MultisigZeroThreshold`, `ERC7579MultisigUnreachableThreshold`).

A failure of check 2 or 3 does not revert the owner's transaction. The queue catches it: the transaction succeeds, the identity's queue emits `ExecutionFailed`, and nothing changes. The error of the module is not returned to the caller.

**What changes:** The identity's queue emits `Executed`, and the module updates the settings of this identity. A change of guardians, of weights, of threshold or of expiration applies at once. A change of delay does not. A longer delay takes effect after the new delay itself, capped at 5 days. A shorter delay takes effect after the difference between the current delay and the new one, so a recovery can never execute sooner than the delay that applied when the change was made. Until then, `getDelay(identity)` returns the current delay, the pending one and the time it takes effect.

## State after the flow

* `Identity.isModuleInstalled(2, recoveryModule, "")` is true.
* `Identity.keyHasPurpose(keyHash, 1)` is true for the module's key.
* `RecoveryModule.getGuardianCount(identity)`, `totalWeight(identity)`, `threshold(identity)`, `getDelay(identity)` and `getExpiration(identity)` return the settings the owner chose.
* `RecoveryModule.isGuardian(identity, guardian)` is true for each guardian.
* The identity's own keys, claims, linked wallets and positions are unchanged.
* No recovery is scheduled. The guardians have no authority until they sign one together.

## Failure modes

| Symptom                                                                                                    | Cause                                                                                                                                                                                                                      | Fix                                                                                           |
| ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `installModule` reverts with `SenderDoesNotHaveManagementKey`                                              | The caller does not hold `MANAGEMENT` on the identity (step 2, check 1)                                                                                                                                                    | Submit with a `MANAGEMENT` key                                                                |
| `installModule` reverts with `ERC7579MismatchedModuleTypeId`                                               | The module does not declare the module type that was passed (step 2, check 2)                                                                                                                                              | Use the address of a `RecoveryModule` and module type 2                                       |
| `installModule` reverts with `ERC7579AlreadyInstalledModule`                                               | The module is already installed on this identity (step 2, check 3)                                                                                                                                                         | Change its settings with step 5                                                               |
| `installModule` reverts with `ERC7579DelayedExecutorAlreadyInstalled` or `ERC7579MultisigAlreadyInstalled` | The module already holds settings for this identity (step 2, check 4)                                                                                                                                                      | Change them with step 5                                                                       |
| `installModule` reverts with `ERC7579MultisigInvalidInitData` or `ERC7579MultisigEmptySigners`             | The guardian arguments are malformed, or list no guardian (step 2, check 5)                                                                                                                                                | Rebuild `initData` as in step 1                                                               |
| `installModule` reverts with `ERC7579MultisigInvalidSigner` or `ERC7579MultisigAlreadyExists`              | A guardian is shorter than 20 bytes, or listed twice (step 2, check 6)                                                                                                                                                     | Pass each guardian once, as signer data                                                       |
| `installModule` reverts with `ERC7579MultisigMismatchedLength` or `ERC7579MultisigInvalidWeight`           | The weights do not match the guardians one for one, or a weight is zero (step 2, check 7)                                                                                                                                  | Give each guardian a weight of at least 1                                                     |
| `installModule` reverts with `ERC7579MultisigZeroThreshold` or `ERC7579MultisigUnreachableThreshold`       | The threshold is zero, or above the total weight (step 2, check 8)                                                                                                                                                         | Choose a threshold between 1 and the total weight                                             |
| `addKeyWithData` reverts with `InvalidSignerData`                                                          | `keyHash` is not the hash of the module's 20-byte address (step 3)                                                                                                                                                         | Compute `keyHash = keccak256(abi.encodePacked(recoveryModule))`                               |
| `addKeyWithData` reverts with `KeyAlreadyRegistered`                                                       | The module already holds `MANAGEMENT` (step 3)                                                                                                                                                                             | Nothing to do                                                                                 |
| A recovery executes and reverts with `ExecutorPurposeNotAuthorized`                                        | The module was installed and step 3 was never done                                                                                                                                                                         | Do step 3 while a `MANAGEMENT` key exists. Check it in step 4                                 |
| A change of settings has no effect and no `Executed` event appears                                         | The request was queued by a key that does not hold `MANAGEMENT` (step 5, check 1)                                                                                                                                          | Submit with a `MANAGEMENT` key                                                                |
| The transaction succeeds, `ExecutionFailed` is emitted and the settings are unchanged                      | The call targeted the module directly and not through the identity calling itself (step 5, check 2), or the module refused the change, for example a removal after which the threshold cannot be reached (step 5, check 3) | Wrap the call as in step 5. Lower the threshold first, or add the replacement before removing |
| A user operation that targets the module reverts with `OwnModuleTargetBlocked`                             | The identity does not dispatch a call into its own modules (step 5, check 2)                                                                                                                                               | Make the identity call itself, as in step 5                                                   |
| A new delay does not apply                                                                                 | A change of delay waits for its transition period (step 5)                                                                                                                                                                 | Read `getDelay(identity)` for the pending value and its effect time                           |

## Common misunderstandings

> **Wrong:** "Installing the module is enough."
>
> **Right:** Installation stores the guardians and the settings. The module can only add a key to the identity once it holds the `MANAGEMENT` purpose itself, as a key of type `MODULE`. Without step 3, a recovery is scheduled, waits for its delay, and fails when it executes.

> **Wrong:** "Guardians can replace one another."
>
> **Right:** The guardian set and the settings change only by a call from the identity itself, which takes a `MANAGEMENT` key. Guardians can do two things together: authorize an operation that executes after the delay, and cancel a scheduled one.

> **Wrong:** "The module is one more manager of the identity."
>
> **Right:** A key of type `MODULE` cannot sign anything. It lets the module execute what the guardians authorized, after the delay. It does not count when the identity checks that a `MANAGEMENT` signer remains.

> **Wrong:** "A guardian has to be an ONCHAINID."
>
> **Right:** A guardian is a signer: an address, or any other signer type the identity layer verifies. It needs no identity, no key on the protected identity and no role anywhere.

## Integration notes

* Offer this flow in the same session as the creation of the identity ([02](/guides/identity/02.-deploy-an-onchainid.md)). Steps 2 and 3 can be sent together. An identity that loses its keys before step 3 has no on-chain recovery.
* Always run step 4, and show the owner the result. A module installed without the `MANAGEMENT` purpose looks configured and cannot recover anything.
* One `RecoveryModule` serves every identity, like every module of the identity layer. It keeps its settings per identity, and a guardian's signature is bound to the identity it was made for.
* When the executor arguments are left out of the installation data, the module applies its defaults: a delay of 5 days and an expiration of 60 days.
* Choose the delay with the owner. It is the time the owner has to cancel a recovery it did not ask for. Monitor scheduled recoveries on every identity your application serves.
* Guardians are stored as signer data. For an address, pass `abi.encodePacked(address)`, in the installation data and in every later call.
* Replace a guardian by adding the new one before removing the old one, so that the threshold stays reachable at every step.
* After each change, read the settings again as in step 4. A change that the module refused does not revert the transaction: it shows as `ExecutionFailed` on the identity's queue.
* After a recovery, review the guardians: the event that cost the keys may have exposed them too.
* Uninstalling the module with `Identity.uninstallModule(2, recoveryModule, "")` removes the purposes the module holds and its guardian settings for the identity. The identity then has no on-chain recovery until the module is installed again. The delay is not forgotten at once: it winds down over its own length, and an installation made in the meantime follows the transition rules of step 5, so uninstalling and reinstalling cannot shorten the delay at once.
* Index the module's events, filtered by identity, to follow a configuration: `ERC7913SignerAdded`, `ERC7913SignerRemoved`, `ERC7579MultisigWeightChanged`, `ERC7913ThresholdSet`, `ERC7579ExecutorDelayUpdated`, `ERC7579ExecutorExpirationUpdated`.

## Related flows

* [21 Recover access to an ONCHAINID](/guides/identity/21.-recover-access-to-an-onchainid.md) : what the guardians do when the keys are lost
* [02 Deploy an ONCHAINID](/guides/identity/02.-deploy-an-onchainid.md) : the moment to install the module
* [22 Add and remove keys on an ONCHAINID](/guides/identity/22.-add-and-remove-keys-on-an-onchainid.md) : the key operations used in step 3


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.t-rex.network/guides/identity/28.-install-the-recovery-module-and-manage-guardians.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
