RADIX WikiRADIX Wiki

Introduction

An Access Controller is a native blueprint that holds a smart account's owner badge in a vault and hands out proofs of it under rules the ledger enforces. It is the primitive behind the Radix Wallet's Security Shield: instead of an account being whatever a single private key says it is, the account is controlled by a component with three roles, a proposal process for changing them, and an optional time delay that no one can shorten.

That matters most in the case it was built for. A resource like XRD is created without the recall feature, so a stolen balance cannot be pulled back by anyone — not the issuer, not a validator vote, not a protocol update. The ledger's answer to a compromised key is therefore not to reverse the theft but to change who controls the account, and the Access Controller is the mechanism that makes that change contestable, delayed, and visible on-ledger while it happens.

Three roles, and who may propose

The blueprint defines exactly three roles — Primary, Recovery and Confirmation — in a Role enum alongside a second, smaller enum: Proposer, whose only variants are Primary and Recovery. The confirmation role can neither initiate nor propose anything. Its entire power is to agree with somebody else.

Primary is the day-to-day role. It is the only role that can call create_proof, the method that produces a proof of the owner badge and therefore lets the account be used at all — and it can only do so while primary is unlocked; otherwise the engine returns OperationRequiresUnlockedPrimaryRole. Recovery and confirmation do nothing during normal operation.

A recovery is a proposal to replace the whole RuleSet — a new access rule for each of the three roles at once — optionally together with a new timed-recovery delay. Both primary and recovery can initiate one, and the role assignment on the blueprint arranges that neither can then wave its own proposal through: a proposal from primary is confirmable by recovery or confirmation, and a proposal from recovery is confirmable by primary or confirmation. Two of the three roles must agree on any change of control, and the pairing is never optional.

Quick confirmation and the timed path

The fast route is quick_confirm: a second role signs, the rule set is written, and control has moved within one transaction. It requires a counterparty, which is the point — it is the route for a user who still holds two of their three factors.

The slow route exists for the user who holds only one. When the recovery role initiates a recovery and the controller was created with a timed_recovery_delay_in_minutes, the engine stamps the proposal with timed_recovery_allowed_after = now + delay, read from on-ledger time at minute precision. Once that instant passes, timed_confirm_recovery completes the recovery with no second role involved. The method carries MethodAccessibility::Public in the blueprint's auth template, so the confirming transaction needs no badge at all; what it cannot do is alter the proposal, which is validated against the one already recorded. A proposal from the primary role never gets a timer — the timed path belongs to the recovery role alone.

The delay is stored as a u32 of minutes, which the source notes tops out at 4,294,967,295 minutes, "which is 8171.5511700913 years"; set it to None and there is no timed path at all. Whatever the value, it is the window in which the legitimate holder of the primary or confirmation role can cancel the proposal — which is why an attacker who has stolen one factor cannot simply wait a stolen account out in silence. Every step emits an event — InitiateRecoveryEvent, RuleSetUpdateEvent, CancelRecoveryProposalEvent — so the whole contest is legible in the account's on-ledger history rather than only in a wallet's notifications.

Locking primary, and retiring the controller

Two further powers sit outside the recovery flow. The recovery role alone can call lock_primary_role, and while primary is locked create_proof fails — so a holder who knows their signing key is compromised can freeze the account's normal use without yet having agreed on who should control it next. Pending recoveries continue while it is locked; stop_timed_recovery, by contrast, is callable by all three roles.

The exit is badge_withdraw. Either proposer can initiate it, and confirming it hands the controlled owner badge back as an ordinary bucket — after which the engine rewrites all three roles to DenyAll. The controller does not merely release the account; it permanently retires itself, and the badge it was holding is once again just a badge in somebody's wallet, with all the exposure that implies.

Recovery badges, and who pays for a recovery

Recovery and confirmation are usually not keys the user carries. When a controller is created it mints its own non-fungible recovery badge resource, owned by the controller's own global caller badge so that nothing else can ever mint one, and mint_recovery_badges is callable by primary or recovery to issue further badges to guardians, a second device, or a service.

That raises a problem the first version of the blueprint could not solve: a guardian holding a recovery badge is not the account's owner and may hold no XRD of their own, yet somebody has to pay the transaction fee for every step of a recovery — and the account's own XRD is precisely what is out of reach. The Bottlenose protocol update fixed it by migrating every existing controller to a second version of the blueprint carrying an xrd_fee_vault, via a state update named, plainly enough, update_access_controller_to_add_xrd_fee_vault. Any of the three roles may lock_recovery_fee against that vault; only primary may withdraw from it; and contribute_recovery_fee is public, so anyone at all can top up a stranger's recovery fund without gaining any say over the account. A live read of the native package on mainnet at epoch 332766 shows all 22 methods, the three fee-vault methods included, exported from the single AccessController blueprint.

What it is not

An Access Controller changes who controls an account. It does not reverse anything that already left. Assets a thief moved out during the window before recovery completes are gone in the ordinary way, because the resources themselves carry no recall path — a distinction worth keeping sharp, since the recurring community proposal to "mark" tokens with their owner so they can be clawed back founders on the same point from the other side: a unit that remembers its holder is, by definition, no longer fungible.

Nor is it free of assumptions. It presumes the user still holds at least one factor, that the recovery factor was set up before it was needed, and that the delay chosen is long enough to notice an attack in but short enough to be usable. Those are the same trade-offs a bank's fraud desk makes; the difference is that here they are parameters on a component anyone can read, rather than a policy on somebody else's screen.

HydrateLast updated Aug 7, 2026v2.0.03 revisionsVerified Aug 7, 2026