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

# Compliance & access control

> Configure whitelisting, KYC gating, and role-based permissions for your Conduit

Railnet uses **External Access Control (EAC)** — a flexible role system that supports global, scoped, and public roles. As a Conduit owner, you configure EAC to control who can operate strategies, execute STEAM queries, manage fees, and more.

## Role types

<AccordionGroup>
  <Accordion title="Global roles">
    Standard roles that apply across the entire protocol. If you grant an account the `VEHICLE_STEAM_DEPOSIT` role globally, they can create deposit queries on **all** yield sources. `VEHICLE_STEAM_REDEEM` works the same way for redeem operations.

    <CodeGroup>
      ```solidity Solidity theme={null}
      accessControl.grantRole(Roles.VEHICLE_STEAM_DEPOSIT, operatorAddress);
      accessControl.grantRole(Roles.VEHICLE_STEAM_REDEEM, operatorAddress);
      ```

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

  <Accordion title="Scoped roles">
    Roles restricted to a **specific contract**. Grant the `VEHICLE_STEAM_DEPOSIT` role scoped to a single yield source, and the account can only create deposit queries on that source.

    <CodeGroup>
      ```solidity Solidity theme={null}
      accessControl.grantScopedRole(
          Roles.VEHICLE_STEAM_DEPOSIT,
          address(myVehicle),  // scope
          operatorAddress
      );

      // VEHICLE_STEAM_REDEEM can be granted independently
      accessControl.grantScopedRole(
          Roles.VEHICLE_STEAM_REDEEM,
          address(myVehicle),
          operatorAddress
      );
      ```

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

    Scoped roles are encoded as `keccak256(abi.encodePacked(role, scope))`.
  </Accordion>

  <Accordion title="Public roles">
    Roles effectively granted to everyone. When a role is public, `hasRole` checks always return `true` regardless of the account. Deposits and redeems can be opened independently.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Open deposits on a specific Vehicle
      accessControl.setScopedRolePublic(
          Roles.VEHICLE_STEAM_DEPOSIT,
          address(myVehicle),
          true
      );

      // Open redeems on the same Vehicle
      accessControl.setScopedRolePublic(
          Roles.VEHICLE_STEAM_REDEEM,
          address(myVehicle),
          true
      );
      ```

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

    <Warning>
      The `DEFAULT_ADMIN_ROLE` can never be made public.
    </Warning>
  </Accordion>
</AccordionGroup>

## Deploy and configure access control

Every Conduit needs an EAC contract. This section walks you through deploying one and granting the roles your product needs.

<Steps>
  <Step title="Deploy ExternalAccessControl">
    Deploy the EAC contract with your initial admin. Set `initialDelay` to a non-zero value (e.g. 48 hours) for production deployments — this protects admin transfers with a time delay.

    <CodeGroup>
      ```solidity Solidity theme={null}
      ExternalAccessControlFactory.SpawnParams memory params = ExternalAccessControlFactory.SpawnParams({
          initialDelay: 48 hours,              // Time-delayed admin transfer for security
          initialDefaultAdmin: platformAdmin,   // Your platform's admin address (use multisig)
          initialRoles: new ExternalAccessControlFactory.RoleAttribution[](0),
          deploymentSalt: keccak256("platform-eac-v1")
      });

      ExternalAccessControl eac = eacFactory.spawn(params);
      ```

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

    <Tip>
      Use a multisig wallet (e.g. Safe) as the `initialDefaultAdmin`. This is the most privileged role in the system — it can grant and revoke any role.
    </Tip>
  </Step>

  <Step title="Grant operator roles">
    Authorize your operators and asset managers for the specific strategies and yield sources they manage.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Grant STEAM access scoped to a specific Multi-Vehicle
      eac.grantScopedRole(Roles.VEHICLE_STEAM_DEPOSIT, address(multiVehicle), operator);
      eac.grantScopedRole(Roles.VEHICLE_STEAM_REDEEM, address(multiVehicle), operator);

      // Grant Multi-Vehicle management roles
      eac.grantScopedRole(Roles.MULTI_VEHICLE_DEPOSIT, address(multiVehicle), operator);
      eac.grantScopedRole(Roles.MULTI_VEHICLE_DISPATCH, address(multiVehicle), operator);
      eac.grantScopedRole(Roles.MULTI_VEHICLE_REBALANCE, address(multiVehicle), operator);
      ```

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

  <Step title="Authorize yield sources">
    Allow the strategy to interact with specific yield sources.

    <CodeGroup>
      ```solidity Solidity theme={null}
      // Authorize a Vehicle in the Multi-Vehicle registry
      eac.grantScopedRole(
          Roles.MULTI_VEHICLE_SET_VEHICLE_AUTHORIZATION,
          address(multiVehicle),
          platformAdmin
      );
      ```

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

  <Step title="Configure fee management roles">
    <CodeGroup>
      ```solidity Solidity theme={null}
      // Allow updating fees
      eac.grantScopedRole(Roles.FEE_MANAGER_SET_FEES, address(feeManager), feeAdmin);

      // Allow distributing collected fees
      eac.grantScopedRole(Roles.FEE_MANAGER_DISPATCH_ERC20, address(feeManager), operator);
      ```

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

## Key roles reference

### Yield source operations

| Role                        | Allows                                                                 |
| --------------------------- | ---------------------------------------------------------------------- |
| `VEHICLE_STEAM_DEPOSIT`     | `create()`, `resume()`, `unlock()`, `recover()` on **deposit** queries |
| `VEHICLE_STEAM_REDEEM`      | `create()`, `resume()`, `unlock()`, `recover()` on **redeem** queries  |
| `VEHICLE_SET_INTERCEPTIONS` | Configure reward interception rules                                    |
| `VEHICLE_ALLOW`             | Manage module allowlist                                                |

### Strategy management

| Role                                      | Allows                              |
| ----------------------------------------- | ----------------------------------- |
| `MULTI_VEHICLE_DEPOSIT`                   | Direct deposits into accounting     |
| `MULTI_VEHICLE_DISPATCH`                  | Send assets to yield sources        |
| `MULTI_VEHICLE_REBALANCE`                 | Rebalance across positions          |
| `MULTI_VEHICLE_MOVE_ASSETS`               | Move assets between sectors         |
| `MULTI_VEHICLE_MOVE_SHARES`               | Move shares between sectors         |
| `MULTI_VEHICLE_SET_VEHICLE_AUTHORIZATION` | Authorize/deauthorize yield sources |
| `MULTI_VEHICLE_SET_QUEUES`                | Configure queue parameters          |
| `MULTI_VEHICLE_SET_THRESHOLDS`            | Set operational thresholds          |
| `MULTI_VEHICLE_PROGRESS_QUERY`            | Advance sub-query state             |

### Fee management

| Role                                | Allows                    |
| ----------------------------------- | ------------------------- |
| `FEE_MANAGER_SET_FEES`              | Update fee percentages    |
| `FEE_MANAGER_SET_FEE_RECIPIENTS`    | Update fee recipients     |
| `FEE_MANAGER_DISPATCH_ERC20`        | Distribute collected fees |
| `FEE_MANAGER_REDEEM_VEHICLE_SHARES` | Redeem fee shares         |

### Infrastructure

| Role                              | Allows                             |
| --------------------------------- | ---------------------------------- |
| `FACTORY_SPAWN`                   | Deploy new contracts via factories |
| `BEACON_UPGRADE`                  | Upgrade beacon implementations     |
| `BEACON_FREEZE`                   | Permanently freeze a beacon        |
| `BEACON_PAUSE` / `BEACON_UNPAUSE` | Pause/unpause beacons              |

## Checking permissions

<CodeGroup>
  ```solidity Solidity theme={null}
  // Check global role
  bool hasAccess = eac.hasRole(Roles.VEHICLE_STEAM_DEPOSIT, account);

  // Check scoped role
  bool hasScopedAccess = eac.hasScopedRole(
      Roles.VEHICLE_STEAM_DEPOSIT,
      address(myVehicle),
      account
  );

  // Check either global or scoped
  bool hasAnyAccess = eac.hasRoleOrScopedRole(
      Roles.VEHICLE_STEAM_DEPOSIT,
      address(myVehicle),
      account
  );
  ```

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

## Admin transfer

The `DEFAULT_ADMIN_ROLE` uses a **time-delayed transfer** for security:

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

  <Step title="Wait for delay">
    The configured delay (e.g., 48 hours) must pass.
  </Step>

  <Step title="Accept transfer">
    The new admin calls:

    ```solidity theme={null}
    eac.acceptDefaultAdminTransfer();
    ```
  </Step>
</Steps>

<Tip>
  Use scoped roles whenever possible. They provide granular control and limit the blast radius if an account is compromised.
</Tip>
