> ## 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.

# Access control and roles

> Role-based permissions and external access control in Railnet

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

External Access Control (EAC) is Railnet's central permission system. Based on OpenZeppelin's `AccessControlDefaultAdminRules`, it manages permissions across all vehicles and protocol components with granular, auditable control.

## Role types

EAC supports three distinct types of roles to provide flexible permission management.

### Global roles

Global roles are standard `bytes32` identifiers that apply across the entire protocol. When you grant an account a global role, it holds that permission for all protocol components that check for it.

<CodeGroup>
  ```solidity Solidity theme={null}
  // Grant a global role
  eac.grantRole(VEHICLE_STEAM_DEPOSIT, aliceAddress);

  // Check a global role
  bool hasAccess = eac.hasRole(VEHICLE_STEAM_DEPOSIT, aliceAddress); // true for all vehicles

  // VEHICLE_STEAM_REDEEM works identically for redeem operations
  eac.grantRole(VEHICLE_STEAM_REDEEM, aliceAddress);
  ```

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

### Scoped roles

Scoped roles are restricted to a specific contract address (the scope). This allows fine-grained permissions, such as granting an account the ability to manage a specific vehicle without giving it permissions over all vehicles.

Internally, a scoped role is represented as `keccak256(abi.encodePacked(role, scope))`.

<CodeGroup>
  ```solidity Solidity theme={null}
  // Grant VEHICLE_STEAM_DEPOSIT to Alice for a specific vehicle only
  eac.grantScopedRole(VEHICLE_STEAM_DEPOSIT, vehicleAddress, aliceAddress);

  // Alice has deposit access to this specific vehicle
  bool hasAccess = eac.hasScopedRole(VEHICLE_STEAM_DEPOSIT, vehicleAddress, aliceAddress); // true

  // But NOT to other vehicles
  bool hasGlobal = eac.hasRole(VEHICLE_STEAM_DEPOSIT, aliceAddress); // false

  // VEHICLE_STEAM_REDEEM works the same way for redeem operations
  eac.grantScopedRole(VEHICLE_STEAM_REDEEM, vehicleAddress, aliceAddress);
  ```

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

### Public roles

Public roles are effectively granted to everyone. When you make a role public, `hasRole` and `hasScopedRole` checks for that role return `true` for any account.

<CodeGroup>
  ```solidity Solidity theme={null}
  // Make VEHICLE_STEAM_DEPOSIT public for a specific vehicle
  eac.setScopedRolePublic(VEHICLE_STEAM_DEPOSIT, vehicleAddress, true);

  // Now anyone can deposit on that vehicle
  bool anyoneHasAccess = eac.hasScopedRole(VEHICLE_STEAM_DEPOSIT, vehicleAddress, bob); // true

  // VEHICLE_STEAM_REDEEM can be made public separately for redeem operations
  eac.setScopedRolePublic(VEHICLE_STEAM_REDEEM, vehicleAddress, true);
  ```

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

<Warning>
  The `DEFAULT_ADMIN_ROLE` cannot be made public.
</Warning>

## Checking permissions

EAC provides three methods for checking access:

| Method                                      | Checks                                 | Use when                               |
| ------------------------------------------- | -------------------------------------- | -------------------------------------- |
| `hasRole(role, account)`                    | Global role only (or public)           | You need protocol-wide access          |
| `hasScopedRole(role, scope, account)`       | Scoped role only (or public for scope) | You need access to a specific contract |
| `hasRoleOrScopedRole(role, scope, account)` | Either global OR scoped                | Most common -- allows both patterns    |

## Admin management

EAC implements a secure, time-delayed mechanism for transferring the default admin role:

<Steps>
  <Step title="Initiate transfer">
    The current admin calls `beginDefaultAdminTransfer(newAdmin)` to start the transfer process.
  </Step>

  <Step title="Wait for delay">
    A configurable time delay must pass before the transfer can complete.
  </Step>

  <Step title="Accept transfer">
    The pending admin calls `acceptDefaultAdminTransfer()` to complete the transfer.
  </Step>
</Steps>

The delay period can be adjusted using `changeDefaultAdminDelay(newDelay)`.

## Role reference

### Factory roles

| Role                       | Description                                                                                                |
| -------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `FACTORY_SPAWN`            | Deploy new vehicles and conduits via factory                                                               |
| `FACTORY_SET_DEPRECATED`   | Disable a factory to prevent future deployments                                                            |
| `ASSET_REGISTRY_SET_ASSET` | Authorize, deauthorize, or reconfigure assets in the [AssetRegistry](/developers/contracts/asset-registry) |

### Beacon and proxy roles

| Role             | Description                                    |
| ---------------- | ---------------------------------------------- |
| `BEACON_UPGRADE` | Upgrade the implementation address of a beacon |
| `BEACON_FREEZE`  | Permanently freeze a beacon (irreversible)     |
| `BEACON_PAUSE`   | Pause beacon operations                        |
| `BEACON_UNPAUSE` | Resume paused beacon operations                |

### Vehicle roles

| Role                        | Description                                                                                    |
| --------------------------- | ---------------------------------------------------------------------------------------------- |
| `VEHICLE_STEAM_DEPOSIT`     | Interact with STEAM functions (`create`, `resume`, `unlock`, `recover`) on **deposit** queries |
| `VEHICLE_STEAM_REDEEM`      | Interact with STEAM functions (`create`, `resume`, `unlock`, `recover`) on **redeem** queries  |
| `VEHICLE_SET_INTERCEPTIONS` | Configure reward interception rules                                                            |
| `VEHICLE_ALLOW`             | Manage the allowlist of modules in the vehicle's ModulesManager                                |

<Info>
  STEAM authorization is split across deposit and redeem so operators can gate each direction independently — for example, keeping deposits open while pausing redemptions during a strategy wind-down, or restricting deposits to KYC'd addresses while exits remain public.
</Info>

### FeeManager roles

| Role                                | Description                        |
| ----------------------------------- | ---------------------------------- |
| `FEE_MANAGER_SET_FEES`              | Update fee percentages             |
| `FEE_MANAGER_SET_FEE_RECIPIENTS`    | Update fee recipient addresses     |
| `FEE_MANAGER_DISPATCH_ERC20`        | Distribute collected fees          |
| `FEE_MANAGER_REDEEM_VEHICLE_SHARES` | Redeem vehicle shares held as fees |

### Multi-Vehicle roles

| Role                                               | Description                                    |
| -------------------------------------------------- | ---------------------------------------------- |
| `MULTI_VEHICLE_SET_VEHICLE_AUTHORIZATION`          | Authorize or deauthorize sub-vehicles          |
| `MULTI_VEHICLE_MOVE_ASSETS`                        | Move assets between sectors                    |
| `MULTI_VEHICLE_MOVE_SHARES`                        | Move shares between sectors                    |
| `MULTI_VEHICLE_DISPATCH`                           | Dispatch assets to sub-vehicles                |
| `MULTI_VEHICLE_SET_QUEUES`                         | Configure deposit and redeem queues            |
| `MULTI_VEHICLE_REBALANCE`                          | Rebalance between vehicles                     |
| `MULTI_VEHICLE_DEPOSIT`                            | Deposit assets into the accounting engine      |
| `MULTI_VEHICLE_PROGRESS_QUERY`                     | Advance sub-query states in the SubQueryEngine |
| `MULTI_VEHICLE_SET_THRESHOLDS`                     | Configure operational thresholds               |
| `MULTI_VEHICLE_FEED_QUERY_REDEEM_QUEUE`            | Add pending queries to the redeem queue        |
| `MULTI_VEHICLE_RETRIEVE_QUERY_REDEEM_QUEUE_ASSETS` | Retrieve assets from the redeem queue          |

### ModulesManager roles

| Role              | Description                                       |
| ----------------- | ------------------------------------------------- |
| `EXEC`            | Execute a module on a target                      |
| `MODULE_MANAGER`  | Add, update, or remove modules                    |
| `CANCEL_MODULE`   | Cancel a pending module operation during timelock |
| `UPDATE_TIMELOCK` | Update the global timelock duration               |

### Keeper roles

| Role                     | Description                                |
| ------------------------ | ------------------------------------------ |
| `JOB_LISTING_REGISTER`   | Register new keeper jobs                   |
| `JOB_LISTING_UNREGISTER` | Remove registered keeper jobs              |
| `JOB_LISTING_EXECUTE`    | Execute registered jobs                    |
| `KEEPER_ON_REPORT`       | Forward job reports to the Keeper contract |

## FreezablePausableBeacon

The EAC also manages the implementation address for beacon proxies (vehicle clones) through the FreezablePausableBeacon. This component has two critical states:

<Tabs>
  <Tab title="Freeze">
    A **permanent and irreversible** state. Once frozen, the implementation address can never be upgraded again. This provides a "trustless" guarantee that the contract logic is immutable.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Permanently freeze the beacon (irreversible)
      eac.freeze();
      // Requires BEACON_FREEZE role
      ```

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

  <Tab title="Pause">
    A **temporary and reversible** state. While paused, the `implementation()` method reverts, effectively disabling all proxies that rely on this beacon.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Pause indefinitely
      eac.pause();

      // Or pause for a specific duration
      eac.pauseFor(7 days);

      // Resume operations
      eac.unpause();
      ```

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

## Common permission patterns

<AccordionGroup>
  <Accordion title="Grant an operator access to a specific Multi-Vehicle">
    Use scoped roles to restrict the operator to a single Multi-Vehicle:

    <CodeGroup>
      ```solidity Solidity theme={null}
      eac.grantScopedRole(MULTI_VEHICLE_DISPATCH, multiVehicleAddress, operatorAddress);
      eac.grantScopedRole(MULTI_VEHICLE_SET_QUEUES, multiVehicleAddress, operatorAddress);
      eac.grantScopedRole(MULTI_VEHICLE_REBALANCE, multiVehicleAddress, operatorAddress);
      ```

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

  <Accordion title="Open a vehicle to public STEAM queries">
    Make the STEAM roles public for a specific vehicle so any user can interact. Deposits and redeems can be opened independently:

    <CodeGroup>
      ```solidity Solidity theme={null}
      eac.setScopedRolePublic(VEHICLE_STEAM_DEPOSIT, vehicleAddress, true);
      eac.setScopedRolePublic(VEHICLE_STEAM_REDEEM, vehicleAddress, true);
      ```

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

  <Accordion title="Set up a fee manager operator">
    Grant the roles needed to manage and distribute fees:

    <CodeGroup>
      ```solidity Solidity theme={null}
      eac.grantScopedRole(FEE_MANAGER_SET_FEES, feeManagerAddress, operatorAddress);
      eac.grantScopedRole(FEE_MANAGER_SET_FEE_RECIPIENTS, feeManagerAddress, operatorAddress);
      eac.grantScopedRole(FEE_MANAGER_DISPATCH_ERC20, feeManagerAddress, operatorAddress);
      eac.grantScopedRole(FEE_MANAGER_REDEEM_VEHICLE_SHARES, feeManagerAddress, operatorAddress);
      ```

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