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

# Security Properties

> The concrete on-chain checks that make settlement paths safe to integrate.

Ryvo enforces strict checks around permanent identities, bucketed channels, and off-chain signed settlement. This page covers the checks builders care about most.

## Deployment-scoped signing domain

Every signed message includes a 16-byte `message_domain`.

`message_domain` is derived **inside the program** from:

1. a fixed Ryvo domain tag
2. the program ID
3. the chain ID

This means the signing domain is bound to the exact deployment. It cannot be set incorrectly by off-chain tooling, and a message signed for one environment will not verify on another.

## Distinct chain IDs per environment

Mainnet, devnet, testnet, and localnet use distinct chain IDs. Because `message_domain` depends on `chain_id`, the derived domain differs across environments by construction.

The practical consequence: a commitment signed for devnet cannot be replayed on mainnet, and vice versa, even if the program ID were otherwise identical.

See [Deployment](/reference/deployment) for the specific chain IDs and message domains.

## Permanent participants and permanent channels

The protocol removed participant recycling and channel close-and-reopen flows. This eliminates a whole class of replay complexity:

* `participant_id` values do not get reused.
* There is no reopened payment channel for an old signed message to target.
* Off-chain services can rely on stable channel identities.

## Canonical PDA checks

Settlement paths do not trust arbitrary program-owned accounts just because they deserialize correctly.

The program re-derives and verifies canonical bucket PDAs for every participant and channel account passed in, including accounts that arrive through `remaining_accounts`. This matters most in `settle_commitment_bundle` and `settle_clearing_round`, where many accounts are passed dynamically.

Concretely, the program checks:

* participant bucket / owner-index bucket addresses against their seed rules
* channel bucket addresses against `["channel-bucket-v2", token_id, bucket_id]` and canonical lane mapping

A passed account that is not the canonical PDA is rejected with `ChannelNotInitialized` or `ParticipantNotFound`.

## Monotonic cumulative settlement

Both unilateral commitments and cooperative rounds must move channel state strictly forward.

If a signed message tries to settle to a cumulative amount that is less than or equal to the current `settled_cumulative`, the program rejects it with `CommitmentAmountMustIncrease`. This is both the replay-protection mechanism for off-chain commitments and the rule that makes "keep only the newest commitment" a safe strategy off-chain.

## Balanced clearing rounds

`settle_clearing_round` requires that the net flow sums across the included participants balance correctly. Rounds with inconsistent deltas are rejected with `NetFlowImbalance`. Participants whose final net position is negative must hold enough total balance to cover it; the program uses saturating checks and returns `InsufficientBalance` or `NetPositionOverflow` on failure.

## Signature verification integrity

Settlement instructions verify signatures and message structure before state writes:

1. Commitment signatures are validated against the exact `ryvo-cmt-v5` body.
2. Clearing-round aggregate signatures are validated against the exact round body.
3. Each signed message parses as a structurally valid protocol message.
4. Settlement instructions are not called via CPI (`CpiNotAllowed`).

This prevents transaction-composition attacks that rely on passing pre-verified but unrelated signatures.

## Timelocked channel maintenance

The protocol places a timelock in front of actions that would otherwise surprise the other side of the relationship:

1. unlocking channel-specific locked funds
2. rotating the channel signing key
3. withdrawing protocol balance to an external token account

Each of these requires a request step and an execution step separated by the configured timelock. The exact durations per environment are on the [Deployment](/reference/deployment) page.

## Self-channel rejection

The protocol explicitly rejects self-channels. This prevents a payer from creating a channel to itself and using settlement to bypass the unlock timing model. A violation returns `SelfChannelNotAllowed`.

## Token-registration safety

Adding a token is an authority-gated instruction. The program rejects:

* duplicate token IDs (`TokenIdAlreadyInUse`)
* duplicate mints (`TokenAlreadyRegistered`)
* zero token IDs (`InvalidTokenId`)
* invalid ASCII symbols (`InvalidTokenSymbol`)
* token decimals above the protocol's supported range (`InvalidTokenDecimals`)
* settlement against token accounts whose mint does not match the registered token (`InvalidTokenMint`)

This prevents most classes of token-confusion attacks where a look-alike mint could be substituted at settlement time.

## Summary

Taken together, these checks mean:

1. Signed messages are bound to one deployment.
2. Account lookups are bound to canonical state.
3. Settlement only moves forward.
4. Channel maintenance cannot surprise the other side instantly.
5. Submission is front-run resistant.
6. Cross-token confusion is caught at registration.

## See also

* [Trust model](/security/trust-model)
* [Design principles](/thesis/design-principles)
* [Message formats](/reference/messages)
* [Error codes](/reference/errors)
