> For AI agents: the documentation index is at https://docs.nimbusprivacy.xyz/llms.txt, and every page is available as Markdown at its URL with .md appended.

# Robinhood governance safeguards (/security/robinhood-governance)

The Robinhood contracts use seconds for governance waits. These safeguards describe the contract
source; applying them to an existing deployment requires the deployment changes described below.

## Proposals and authorities

`MinimumDelayTimelock` preserves OpenZeppelin operation identifiers, predecessor dependencies and
executor roles. Its scheduling floor is one second. A proposal executes from its maturity until
12 hours after maturity, including both boundaries. Execution after that window fails.

Governance can assign a proposer and delay to each contract address and function selector through
`setOperationPolicy`. The assigned proposer must also hold `PROPOSER_ROLE`; an unassigned function
accepts any holder of that role. The global minimum delay still applies, so the effective wait is
the larger of that minimum and the function's configured delay.

Changing a function's authority or increasing its delay advances its queue epoch and invalidates
pending calls under the previous epoch. A reduction under the same authority preserves pending
calls and their original maturity. Every policy replacement has a separate state revision, so an
older queued policy change cannot overwrite a newer one. Revoking a proposer invalidates that
account's pending proposals, even if the role is
granted again. Increasing the global minimum invalidates older pending proposals. Reducing a
function's delay must first wait the longer delay it replaces. Cancellation retains OpenZeppelin's
canceller-role requirement.

The timelock keeps its current proposal, call receipt and batch position in transient storage,
which lasts only for the current transaction. Scheduled proposals, policies, roles and delays
remain in persistent storage. Nested execution saves and restores the outer execution context;
every dispatched call still rechecks its proposal and reviewed state.

## Reviewed state

Targets can expose `governanceState` to bind a queued call to the state it was reviewed against.
The timelock checks that commitment before execution and again before each call in a batch. If an
earlier batch call invalidates a later one, the entire batch reverts.

### Node keys and registration

A node rotation binds the current signer as well as the proposed replacement in the scheduled
calldata. If another rotation changes that signer first, the older rotation fails. Revoking a node
advances a shared registration epoch and invalidates earlier applications and registrations for
both screener and data-endorser committees. A fresh proposal can register after the revocation.

### Deposit asset grants

Revoking a deposit asset advances the pool's shared grant epoch. Every asset grant queued before
that revocation becomes invalid, including grants for another asset. Deposits can be reopened by
a proposal reviewed after the revocation.

## Zone changes

Key rotation, authority transfer, freeze, unfreeze and delay-table replacement each require at
least one second. Execution also applies this floor to legacy zones that stored zero ordinary
delays. The recovery delay is the exception: a zone may choose any value, zero included, and a
recovery waits exactly the delay the zone holds when the recovery opens. The registry sets no
bound on it, so no governance change can shorten a recovery. A zone that chooses zero accepts a
recovery that executes in the block it opens, before its authority can veto it.

Ordinary changes and recoveries have an inclusive 12-hour execution window after maturity. The
zone authority or bootstrap can cancel a pending change; anyone can clear one after it expires.

### Signer-leaf cooldown

Governance sets the cooldown through `setLeafCooldown`, naming the current value it replaces.
Key rotation and unfreeze can change the signer leaf only after that cooldown has elapsed since
the leaf last changed. An unchanged leaf needs no extra wait. Freeze and recovery can change a
leaf during cooldown; the cooldown then starts from that change. Existing registries default to
zero cooldown until governance configures one.

### Shortening a delay

A replacement delay table waits the current delay-change wait and every old delay being reduced,
whichever is longest. For example, suppose recovery waits one day and changing the delay table
normally waits one minute. A proposal to reduce recovery to one minute still waits one day before
installation. Once installed, a newly opened recovery waits the new one minute. An already opened
recovery keeps its original maturity.

## Fee-enclave switches

Activation, deactivation, freeze and unfreeze retain their distinct proposal roles. A role holder
queues the switch through `proposeSwitch`; anyone can execute it after its wait. The proposal uses
the proxy owner's operation policy and global minimum, with the one-second floor when the owner
provides no timelock policy. The execution window is 12 hours after maturity.

Activation binds the reviewed controller and enclave key. Deactivation invalidates earlier
activation proposals, and freeze invalidates earlier unfreeze proposals. Role revocation,
operation-authority reassignment, an operation-delay increase, a global-delay increase or a change of governance owner can also
invalidate a proposal. Restoring a role or lowering the global delay again does not revive it.

A governance owner holding the corresponding switch role can execute its own timelock proposal
without a second waiting period. The registry accepts this route only while that owner is
dispatching the exact target and calldata. A different role holder cannot forge this receipt.
Each of the four timelock switch calls also binds the registry epoch of its corresponding role.
Revocation invalidates those queued calls before execution, including later calls in the same
batch. Revoking a different switch role leaves the proposal valid.

For example, suppose a freeze is queued through the timelock, then its `FREEZE_ROLE` is revoked
and granted again. The older freeze still cannot execute. Governance must review and queue a
fresh freeze after the role is restored.

The proposer or governance admin can cancel a local switch proposal. Anyone can clear an expired
or invalidated one. A consumed proposal cannot execute twice.

## Native refunds and deployment

`PoolProxy` pins the implementation's WETH address when it is constructed. An empty native refund
from that WETH takes a receipt path that reads no proxy storage and does not delegate, allowing
WETH implementations that refund using the transfer stipend. Other senders still follow the
pool implementation's receive checks. An implementation upgrade must preserve the pinned WETH.

The proxy and timelock runtime code is not upgradeable. Existing deployments need a replacement
timelock and a fresh proxy to obtain those runtime safeguards. The registry and pool state added
for the remaining guards is appended to existing storage. Deployment scripts pin the recognized
timelock runtime hash and reject a different implementation. The transient execution-context
change also requires a replacement timelock: it cannot be applied to an existing instance in
place. Its policies and roles must be configured and governance authority transferred under the
existing timelock's rules; queued operations remain with the old instance and do not migrate
automatically.

The pool uses interface version 3 for its 20-slot-only fee-collection proof support. The guardian
registry uses interface version 2: the pool reads its threshold, active committee size and current
status together for transaction processing and registration. The existing full cluster and status
reads remain available, and the combined read preserves their status rules. Current signer
authorisation is still checked at execution, after token inflows for a value operation. Upgrade
the guardian registry and pool together in one timelock batch, with the registry first.

Cluster activation binds the same configuration digest. Its domain, field widths, byte order and
list order are preserved; the contract constructs the encoded configuration in one destination
buffer instead of repeatedly copying a growing prefix. Existing activation approvals therefore
keep the same configuration binding.

The other five upgradeable Robinhood contracts remain at interface version 1. Rust services compile their
expectations from `interface-versions.json`, the CLI imports
that file directly, and the SDK bundles it into its package during the build. Rebuild the services,
CLI and SDK from the deployment's source revision. Wallet clients must also match the current
contract ABI; matching version numbers alone does not establish that their calldata is compatible.
Later incompatible upgrades must advance the affected entries and update their dependants together.
Indexers must apply the archive migration for the new governance events before archiving them.

This page does not prescribe a live pool migration. A fresh proxy requires a separately reviewed
plan for its custody and state.


Last updated 15 October 2026
