> ## Documentation Index
> Fetch the complete documentation index at: https://docs.railnet.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Roles and permissions

> Configure access control for asset managers, platforms, and automation

<Info>This page covers the smart contract implementation details. See [Glossary](/developers/glossary).</Info>

This guide covers practical role setup for common scenarios. For the conceptual overview of External Access Control (EAC), role types, and scoping mechanics, see [Access control and roles](/developers/contracts/access-control).

## Persona-based role sets

### Platform owner setup

The platform retains strategic control while delegating operations.

| Role                                      | Scope                    | Purpose                                    |
| ----------------------------------------- | ------------------------ | ------------------------------------------ |
| `DEFAULT_ADMIN_ROLE`                      | Global                   | Grant/revoke any role, admin transfers     |
| `MULTI_VEHICLE_SET_VEHICLE_AUTHORIZATION` | Sector Accounting Engine | Control which yield sources the MV can use |
| `FEE_MANAGER_SET_FEES`                    | Fee Manager              | Control fee rates                          |
| `FEE_MANAGER_SET_FEE_RECIPIENTS`          | Fee Manager              | Control revenue distribution               |
| `VEHICLE_SET_INTERCEPTIONS`               | Vehicle/Multi-Vehicle    | Control reward interception rules          |

### Asset manager setup

The AM receives operational roles scoped to the specific Multi-Vehicle they manage.

| Role                                               | Scope                    | Purpose                               |
| -------------------------------------------------- | ------------------------ | ------------------------------------- |
| `MULTI_VEHICLE_DISPATCH`                           | Sector Accounting Engine | Send assets to sub-vehicles           |
| `MULTI_VEHICLE_REBALANCE`                          | Sector Accounting Engine | Move between vehicles                 |
| `MULTI_VEHICLE_MOVE_ASSETS`                        | Sector Accounting Engine | Move assets between sectors           |
| `MULTI_VEHICLE_MOVE_SHARES`                        | Sector Accounting Engine | Move shares between sectors           |
| `MULTI_VEHICLE_SET_QUEUES`                         | Queue Strategy Engine    | Configure allocation priorities       |
| `MULTI_VEHICLE_PROGRESS_QUERY`                     | Sub Query Engine         | Advance async queries                 |
| `MULTI_VEHICLE_FEED_QUERY_REDEEM_QUEUE`            | Multi-Vehicle            | Feed the redemption queue             |
| `MULTI_VEHICLE_RETRIEVE_QUERY_REDEEM_QUEUE_ASSETS` | Multi-Vehicle            | Retrieve assets from redemption queue |

### Keeper/automation setup

Keepers automate routine operations like redemption queue processing.

| Role                                               | Scope         | Purpose                           |
| -------------------------------------------------- | ------------- | --------------------------------- |
| `JOB_LISTING_REGISTER`                             | Job Listing   | Register automation jobs          |
| `JOB_LISTING_EXECUTE`                              | Job Listing   | Execute registered jobs           |
| `KEEPER_ON_REPORT`                                 | Keeper        | Submit execution reports          |
| `MULTI_VEHICLE_FEED_QUERY_REDEEM_QUEUE`            | Multi-Vehicle | Automate redemption queue feeding |
| `MULTI_VEHICLE_RETRIEVE_QUERY_REDEEM_QUEUE_ASSETS` | Multi-Vehicle | Automate asset retrieval          |

### Fee collector setup

For an operator or bot that handles fee collection and distribution.

| Role                                | Scope       | Purpose                                 |
| ----------------------------------- | ----------- | --------------------------------------- |
| `FEE_MANAGER_DISPATCH_ERC20`        | Fee Manager | Distribute collected fees to recipients |
| `FEE_MANAGER_REDEEM_VEHICLE_SHARES` | Fee Manager | Redeem fee shares to underlying asset   |

## Role reference matrix

| Operation                      | Required role                                      | Typical holder | Scope                    |
| ------------------------------ | -------------------------------------------------- | -------------- | ------------------------ |
| Deploy via factory             | `FACTORY_SPAWN`                                    | Platform       | Factory                  |
| Authorize vehicles             | `MULTI_VEHICLE_SET_VEHICLE_AUTHORIZATION`          | Platform       | Sector Accounting Engine |
| Move assets between sectors    | `MULTI_VEHICLE_MOVE_ASSETS`                        | AM             | Sector Accounting Engine |
| Move shares between sectors    | `MULTI_VEHICLE_MOVE_SHARES`                        | AM             | Sector Accounting Engine |
| Dispatch to sub-vehicles       | `MULTI_VEHICLE_DISPATCH`                           | AM             | Sector Accounting Engine |
| Rebalance between vehicles     | `MULTI_VEHICLE_REBALANCE`                          | AM             | Sector Accounting Engine |
| Deposit into accounting        | `MULTI_VEHICLE_DEPOSIT`                            | AM             | Sector Accounting Engine |
| Set allocation queues          | `MULTI_VEHICLE_SET_QUEUES`                         | AM             | Queue Strategy Engine    |
| Set operational thresholds     | `MULTI_VEHICLE_SET_THRESHOLDS`                     | Platform / AM  | Sector Accounting Engine |
| Progress sub-queries           | `MULTI_VEHICLE_PROGRESS_QUERY`                     | AM / Keeper    | Sub Query Engine         |
| Feed redemption queue          | `MULTI_VEHICLE_FEED_QUERY_REDEEM_QUEUE`            | AM / Keeper    | Multi-Vehicle            |
| Retrieve from redemption queue | `MULTI_VEHICLE_RETRIEVE_QUERY_REDEEM_QUEUE_ASSETS` | AM / Keeper    | Multi-Vehicle            |
| STEAM deposit operations       | `VEHICLE_STEAM_DEPOSIT`                            | Users (public) | Vehicle / Multi-Vehicle  |
| STEAM redeem operations        | `VEHICLE_STEAM_REDEEM`                             | Users (public) | Vehicle / Multi-Vehicle  |
| Set reward interceptions       | `VEHICLE_SET_INTERCEPTIONS`                        | Platform       | Vehicle                  |
| Manage modules                 | `MODULE_MANAGER`                                   | Platform       | Modules Manager          |
| Execute modules                | `EXEC`                                             | AM / Keeper    | Modules Manager          |
| Update fees                    | `FEE_MANAGER_SET_FEES`                             | Platform       | Fee Manager              |
| Update fee recipients          | `FEE_MANAGER_SET_FEE_RECIPIENTS`                   | Platform       | Fee Manager              |
| Distribute fees                | `FEE_MANAGER_DISPATCH_ERC20`                       | AM / Keeper    | Fee Manager              |
| Redeem fee shares              | `FEE_MANAGER_REDEEM_VEHICLE_SHARES`                | AM / Keeper    | Fee Manager              |

## Common workflows

### Onboard a new asset manager

<CodeGroup>
  ```solidity Solidity theme={null}
  address am = 0x...; // New asset manager address
  ISectorAccountingEngine accounting = MultiVehicle(multiVehicle).accountingEngine();
  IQueueStrategyEngine strategy = accounting.strategyEngine();
  ISubQueryEngine subQueryEngine = accounting.subQueryEngine();

  // Core operational roles
  eac.grantScopedRole(keccak256("MULTI_VEHICLE_DISPATCH"), address(accounting), am);
  eac.grantScopedRole(keccak256("MULTI_VEHICLE_REBALANCE"), address(accounting), am);
  eac.grantScopedRole(keccak256("MULTI_VEHICLE_MOVE_ASSETS"), address(accounting), am);
  eac.grantScopedRole(keccak256("MULTI_VEHICLE_MOVE_SHARES"), address(accounting), am);
  eac.grantScopedRole(keccak256("MULTI_VEHICLE_SET_QUEUES"), address(strategy), am);
  eac.grantScopedRole(keccak256("MULTI_VEHICLE_PROGRESS_QUERY"), address(subQueryEngine), am);
  eac.grantScopedRole(keccak256("MULTI_VEHICLE_FEED_QUERY_REDEEM_QUEUE"), address(multiVehicle), am);
  eac.grantScopedRole(keccak256("MULTI_VEHICLE_RETRIEVE_QUERY_REDEEM_QUEUE_ASSETS"), address(multiVehicle), am);
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

### Offboard an asset manager

<CodeGroup>
  ```solidity Solidity theme={null}
  address am = 0x...; // Asset manager to remove

  eac.revokeScopedRole(keccak256("MULTI_VEHICLE_DISPATCH"), address(accounting), am);
  eac.revokeScopedRole(keccak256("MULTI_VEHICLE_REBALANCE"), address(accounting), am);
  eac.revokeScopedRole(keccak256("MULTI_VEHICLE_MOVE_ASSETS"), address(accounting), am);
  eac.revokeScopedRole(keccak256("MULTI_VEHICLE_MOVE_SHARES"), address(accounting), am);
  eac.revokeScopedRole(keccak256("MULTI_VEHICLE_SET_QUEUES"), address(strategy), am);
  eac.revokeScopedRole(keccak256("MULTI_VEHICLE_PROGRESS_QUERY"), address(subQueryEngine), am);
  eac.revokeScopedRole(keccak256("MULTI_VEHICLE_FEED_QUERY_REDEEM_QUEUE"), address(multiVehicle), am);
  eac.revokeScopedRole(keccak256("MULTI_VEHICLE_RETRIEVE_QUERY_REDEEM_QUEUE_ASSETS"), address(multiVehicle), am);
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

<Tip>
  Check for in-progress queries before offboarding. Pending async operations may require the AM's roles to complete.
</Tip>

### Make STEAM operations public

Allow anyone to deposit and redeem on a Vehicle or Multi-Vehicle. Deposits and redeems can be opened independently:

<CodeGroup>
  ```solidity Solidity theme={null}
  // Open deposits to all users
  eac.setScopedRolePublic(keccak256("VEHICLE_STEAM_DEPOSIT"), address(multiVehicle), true);

  // Open redeems to all users
  eac.setScopedRolePublic(keccak256("VEHICLE_STEAM_REDEEM"), address(multiVehicle), true);
  ```

  ```typescript TypeScript theme={null}
  // Coming soon
  ```
</CodeGroup>

<Note>
  Making the STEAM roles public is typical for vaults open to all users. These roles only control who can create STEAM queries — they do not affect operational roles. Splitting deposit and redeem lets operators pause one direction (e.g. redemptions during a wind-down, or deposits while under compliance review) without affecting the other.
</Note>

### Rotate DEFAULT\_ADMIN\_ROLE

Transfer admin control using the time-delayed mechanism:

<Steps>
  <Step title="Initiate transfer">
    ```solidity theme={null}
    eac.beginDefaultAdminTransfer(newAdmin);
    ```
  </Step>

  <Step title="Wait for the configured delay">
    The delay (set during EAC deployment) must pass before the transfer can complete.
  </Step>

  <Step title="New admin accepts">
    ```solidity theme={null}
    // Called by the new admin
    eac.acceptDefaultAdminTransfer();
    ```
  </Step>
</Steps>

## Decision guidance

### Scoped vs global roles

| Use scoped roles when                              | Use global roles when                         |
| -------------------------------------------------- | --------------------------------------------- |
| You manage multiple MVs with different operators   | A single operator manages everything          |
| You want to limit blast radius of compromised keys | Convenience outweighs granularity             |
| Different teams manage different vehicles          | You are the sole operator of a personal vault |

**Default recommendation:** Always use scoped roles unless you have a specific reason not to.

### Public roles

| Scenario                              | Make public?                                             | Consideration                                                                        |
| ------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| User-facing vault (open deposits)     | Yes — `VEHICLE_STEAM_DEPOSIT` and `VEHICLE_STEAM_REDEEM` | Required for any user to deposit/redeem. Grant independently to pause one direction. |
| Internal strategy (restricted access) | No                                                       | Only whitelisted addresses can interact                                              |
| Keeper automation                     | No                                                       | Grant `JOB_LISTING_EXECUTE` to specific keeper addresses                             |

### Multisig for admin

Use a multisig wallet for `DEFAULT_ADMIN_ROLE` in production. A compromised admin key can grant itself any role and drain the vault.

## Security checklist

* [ ] **Verify scope addresses** before granting — wrong scope means the role won't work as intended
* [ ] **Use multisig** for `DEFAULT_ADMIN_ROLE` in production
* [ ] **Set non-zero `initialDelay`** on the EAC to protect admin transfers
* [ ] **Regularly audit** active roles via the subgraph or on-chain queries
* [ ] **Plan for offboarding** — document which roles each operator holds
* [ ] **Never grant `DEFAULT_ADMIN_ROLE`** to an asset manager
* [ ] **Keep vehicle authorization** with the platform, not the AM

## Troubleshooting

<AccordionGroup>
  <Accordion title="I granted a role but the transaction reverts">
    The most common cause is an incorrect **scope**. Each role must be scoped to the correct contract:

    * Most MV operational roles → scope to the **Sector Accounting Engine** (not the Multi-Vehicle itself)
    * Queue roles → scope to the **Queue Strategy Engine**
    * Fee roles → scope to the **Fee Manager**
    * STEAM roles → scope to the **Vehicle** or **Multi-Vehicle**

    Use `hasRoleOrScopedRole` to verify the grant:

    ```solidity theme={null}
    bool hasAccess = eac.hasRoleOrScopedRole(role, scope, account);
    ```
  </Accordion>

  <Accordion title="Can I scope to Vehicle or Sector Accounting Engine?">
    It depends on the operation:

    * **`VEHICLE_STEAM_DEPOSIT`** and **`VEHICLE_STEAM_REDEEM`** are scoped to the Vehicle or Multi-Vehicle contract
    * **`MULTI_VEHICLE_DISPATCH`**, **`MULTI_VEHICLE_MOVE_ASSETS`**, etc. are scoped to the **Sector Accounting Engine**
    * **`MULTI_VEHICLE_SET_QUEUES`** is scoped to the **Queue Strategy Engine**

    Check the [Role reference matrix](#role-reference-matrix) for the correct scope for each role.
  </Accordion>

  <Accordion title="How do I check what roles an address has?">
    Query the EAC contract directly or use the Railnet subgraph:

    ```solidity theme={null}
    // Check a specific role
    bool hasRole = eac.hasRoleOrScopedRole(role, scope, account);
    ```

    ```graphql theme={null}
    query AccountRoles($address: String!) {
      RoleGranted(where: { account: { _ilike: $address } }) {
        role
        scope
        sender
      }
    }
    ```
  </Accordion>
</AccordionGroup>
