<!--
Sitemap:
- [Welcome to River](/index)
- [Asset-Backed Finance Primer](/concepts/finance-primer)
- [River for Lenders](/lenders/introduction)
- [Depositing](/lenders/depositing)
- [Earning: the Coupon](/lenders/earning)
- [The Accumulating Wrapper](/lenders/wrapper)
- [Withdrawals & Exits](/lenders/withdrawals)
- [Losses & Impairments](/lenders/losses)
- [FAQ](/lenders/faq)
- [Integrate with River](/integrate/get-started)
- [Wrapper Integration](/integrate/wrapper)
- [Class Token Integration](/integrate/class-token)
- [Contract Addresses](/integrate/addresses)
- [Protocol Architecture](/architecture)
- [Actors: Roles & Permissions](/technical/actors)
- [Proxies & Upgradeability](/technical/proxies)
- [Strategies & Reporters](/technical/strategies)
- [Glossary](/concepts/glossary)
- [Dual NAV & Exchange Rates](/accounting/dual-nav)
- [Settlement & Conservation](/accounting/settlement)
- [The Coupon Ledger](/accounting/coupon-ledger)
- [Wrapper Accounting](/accounting/wrapper)
- [Accounting Examples](/accounting/examples)
- [The Tiered Waterfall](/waterfall/)
- [Waterfall Scenarios](/waterfall/scenarios)
- [Security Model](/security)
- [Protocol Invariants](/technical/invariants)
- [List of Assumptions](/technical/assumptions)
- [External Entry Points](/technical/entry-points)
- [Governance](/governance)
- [Contract Reference](/reference/contracts)
- [River Engineering Standards](/reference/engineering-standards)
-->

# Security Model

River's security model starts from a conservative assumption: operational keys can be compromised.

For that reason, roles trigger bounded workflows with constrained control over deal value.
Pricing mode, class order, waterfall policy, reporter freshness, facility headroom, and strategy
movement amounts are either fixed at initialization or checked every time they are used.

## Roles

Authorization is delegated to `RiverAccessControls`, a shared UUPS role module based on
OpenZeppelin `AccessControlEnumerable`. Core contracts do not store role membership directly.

| Role | Can | Cannot |
| --- | --- | --- |
| `KEEPER_ROLE` | Settle, fund strategies, request returns, claim returns, record facility draws and repays, fund coupon, and process exits. | Change pricing mode, reorder classes, change waterfall policy, exceed covenant headroom, mint principal shares to itself, or move assets to arbitrary recipients. |
| `STRUCTURE_ADMIN_ROLE` | Register strategies and reporters, toggle strategy active flags, and perform keeper actions. | Change initialized deal economics such as pricing mode, class order, and waterfall policy. |
| `UPGRADER_ROLE` | Authorize UUPS implementation upgrades. | Bypass proxy checks or initialize implementations directly. |
| `DEFAULT_ADMIN_ROLE` | Grant and revoke roles and set role admins. | This is the root of trust and should be controlled by governance or a multisig. |

## Compromised keeper

The keeper is expected to be a hot operational role. The contracts bound the damage it can do.

| Attempt | Result |
| --- | --- |
| Settle with a favorable pricing mode. | Reverts by design. `settle()` has no pricing-mode argument. |
| Fund a strategy and hide a transit loss. | Balance changes must match reported movement. Shortfalls settle immediately. |
| Create value during a strategy movement. | Reverts with `MovementCreatedValue`. |
| Overdraw a facility. | Reverts with `DrawExceedsHeadroom`. |
| Replay or roll back a report. | Reverts with `ReporterVersionRollback`. |
| Settle against a stale report. | Reverts with `StaleStrategyReport`. |
| Drain assets through a return request. | Reverts with `UnexpectedAssetMovement`. Return requests must not move assets. |
| Deactivate a strategy to trap capital. | New funding is blocked, but returns and claims still work. |
| Claim more coupon than was funded. | Reverts. Only funded coupon intervals are claimable. |
| Pay coupon to itself instead of holders. | No useful effect. Holders pull pro-rata coupon through `claimCoupon`. |

Coupon caveat: `distribute` is `KEEPER_ROLE`-gated and conservation-checked, and today it has no
on-chain per-window cap. A compromised keeper that already holds the asset can over-fund coupon
with its own cash. Because cash is pulled from the caller, this is over-distribution paid by that
caller. On-chain pause and rate-limit safeguards for this path are specified but still pending.

The intended bounded damage from a compromised keeper is delay, poor timing, or self-funded
coupon over-distribution.

## Enforced invariants

Each invariant has an on-chain check and a revert path.

| Invariant | Where enforced | On violation |
| --- | --- | --- |
| Waterfall allocations conserve the input delta. | `Structure._previewSettlement` | `WaterfallNotConserved` |
| Pro-rata allocation conserves the input delta. | `Structure._applyProRataDelta` | `NavUnderflow` if a loss exceeds NAV |
| Class NAV never goes negative. | Signed-delta and waterfall-loss paths | `NavUnderflow` or `LossExceedsNav` |
| Pricing mode is constant per deal. | Stored at `initialize`; no setter exists. | No callable lever |
| Stale reports cannot settle or register. | `_checkedMetrics` | `StaleStrategyReport` |
| Reporter versions are monotonic. | `_checkedMetrics` | `ReporterVersionRollback` |
| Strategy asset movement matches reported amounts. | `fundStrategy` and `claimStrategyReturn` | `AssetMovementMismatch` |
| Strategy movement cannot create reported value. | `_rebaseline` | `MovementCreatedValue` |
| Strategy movement shortfalls reduce NAV immediately. | `_rebaseline` and pro-rata allocation | NAV cannot be overstated |
| Facility draws respect the borrowing base. | `Facility.draw` | `DrawExceedsHeadroom` |
| Facility over-repayment is not silently clamped. | `Facility.repay` | `RepayExceedsDrawn` |
| Principal shares track principal. | Class NAV and coupon ledger separation | Interest cannot inflate principal NAV |
| Coupon cash is conserved. | Funded intervals and `rho` pricing | Token never owes more realized coupon than it holds |

## External calls and reentrancy

* ERC-20 movement paths are `nonReentrant` using OpenZeppelin `ReentrancyGuardTransient`.
* Settlement reads `reporter.metrics` and `waterfall.split` through `view` calls.
* Strategy approvals are exact and cleared after use.
* Mutating functions follow checks-effects-interactions.
* Events emit after state changes complete.

## Upgrades and storage

* Stateful contracts use UUPS proxies behind `ERC1967Proxy`.
* `_authorizeUpgrade` is gated by `UPGRADER_ROLE`.
* Implementation constructors call `_disableInitializers()`.
* Mutable state uses ERC-7201 namespaces such as `river.storage.Structure.v1.0.0`.
* Storage layout changes must append fields or use a new namespace.

Upgrades are for implementation changes. Deal-economic changes such as class order, pricing mode,
waterfall policy, tier sizes, or facility covenant terms are expected to use an explicit migration
to a new deal.

## Module trust levels

| Module | Trust level | Mitigation if faulty or malicious |
| --- | --- | --- |
| Reporter | Trusted per deal. Reports credit marks. | Stale and rollback gates block old data. Movement checks prevent value creation around funding and returns. |
| Waterfall | Trusted per deal and immutable for the `Structure`. | Conservation is checked by the `Structure`, so the module cannot create or destroy value. |
| Servicer | Trusted to compute interest off-chain and fund coupon honestly. | `distribute` pulls cash before crediting. Per-window caps are pending. |
| Wrapper | Detached accumulating ERC-4626 over a class token. | Coupon is counted once; per-holder exit buckets isolate unwraps. |
| Gate | Eligibility policy module. | Claims and cancels bypass gates, keeping owed funds reachable through eligibility changes. |
| Exit queue | Sequencing module. | Non-custodial. The `Structure` owns lock, burn, and pay logic. |
