Overview
An access rule is the unit of authorization on Radix. It is data attached to an object by the Role Assignment module, not code inside a component, and it is evaluated by the Radix Engine auth module before the called method body begins executing. The evidence a rule is tested against lives in the auth zone, a per-call-frame collection of proofs that the engine creates on entry and tears down on exit.
The separation matters because it changes what a reader of a blueprint can know. A rule set is stored state, so anyone can read the current permissions of a deployed component out of the ledger without reading its code, and the engine will refuse an unauthorized call whether or not the blueprint author remembered to check. What it does not do is choose the rule for you: the engine enforces the rule that was declared, so a rule declared too loosely is enforced too loosely (see What the model does not do, below).
The rule grammar
AccessRule has exactly three variants: AllowAll, DenyAll, and Protected(CompositeRequirement). AllowAll authorizes unconditionally, DenyAll fails unconditionally, and everything else is a requirement tree.
A CompositeRequirement is either a single BasicRequirement or an AnyOf / AllOf list of further composite requirements, so rules nest. The five basic requirements are:
Require(resource or non-fungible)– a proof of that resource, or of that specific non-fungible, must be presentAmountOf(amount, resource)– a proof of at least the given amountCountOf(n, list)– proofs of at least n distinct entries from the list, which is how N-of-M multi-signature is expressedAllOf(list)– proofs of every entryAnyOf(list)– a proof of any one entry
The tree is bounded. The execution constants cap it at MAX_ACCESS_RULE_DEPTH of 8 and MAX_COMPOSITE_REQUIREMENTS of 64 nodes, and the Role Assignment package rejects any rule that exceeds either at the point it is set rather than at the point it is checked.
Because the requirements name resources, an authorization token on Radix is an ordinary resource used as a credential rather than a special type. That convention is what the wiki calls a badge.
The auth zone
The auth zone substate holds three separate kinds of evidence, and only the first is what most descriptions mean by "the proofs":
proofs– real proof objects pushed into the zone from a bucket or vaultsimulate_all_proofs_under_resources– resources under which every possible proof is treated as presentimplicit_non_fungible_proofs– virtual badges the engine supplies without anyone creating them
The implicit set is the part with no equivalent elsewhere. A transaction signature places a virtual non-fungible badge under the Ed25519 or secp256k1 signature resource, which is how a rule can require "signed by this key" without any token existing. On top of that the engine derives two caller badges for each frame: package_of_direct_caller, naming the package that made the call, and global_caller, naming the global component or the blueprint function it originated from. A component can therefore gate a method on being called by a particular package or by a particular sibling component, with no shared secret and nothing to pass in.
The zone is per call frame, not per transaction. The auth module creates a fresh auth zone on every CALL_FUNCTION and CALL_METHOD and destroys it when the frame finishes. It links the new zone to its parent only when the call stays inside the same global context; when the call crosses into a different global component the parent is set to None, so proofs do not leak across the boundary. Calling a component does not hand it your badges.
The zone is also addressable directly. Its blueprint interface exposes push, pop, drain, create_proof_of_amount, create_proof_of_non_fungibles, create_proof_of_all, assert_access_rule, and three separate drops: drop_proofs, drop_regular_proofs, and drop_signature_proofs. The last is the one that matters for manifest hygiene, because it removes the signature-derived virtual badges while leaving explicitly created proofs in place.
How a check is resolved
On a method call the engine resolves the method to a permission and then tests it, in that order, before the body runs. The permission comes from the blueprint's own declaration and is one of four kinds: Public, OuterObjectOnly (used by the Validator blueprint to restrict certain methods to the Consensus Manager), OwnPackageOnly, or RoleProtected with a list of role names.
A role name is then resolved to a rule against the object's own role assignment, and the resolution has two special cases worth knowing:
_self_is not stored. It resolves torequire(global_caller(<this object>)), meaning the object calling itself.- An undefined role does not deny. If the role has no entry in the role-assignment key-value store, the engine reads the object's owner role and tests that rule instead. A role a blueprint declares but never assigns is owner-gated, not locked.
The rule is then evaluated against the auth zone of the calling frame, walking up the parent chain where one exists. AllowAll passes, DenyAll fails and returns the rule itself in the failure, and a protected rule is checked node by node against the three kinds of evidence above.
What the model does not do
System-level enforcement removes one class of defect: a method cannot execute with its guard forgotten, because the guard is not in the method. It does not remove authorization bugs as a category, and three limits are worth stating plainly.
- The rule is a design decision.
AllowAllon a mint role is enforced exactly as faithfully as a badge requirement. The engine has no opinion about which was intended. - Undefined roles inherit the owner rule rather than failing closed, per the resolution path above. An incomplete role assignment is a permissive outcome, not a restrictive one.
- Ownership can be mutable or fixed, and the difference is set once. The owner role is declared as
None,Fixed, orUpdatableat instantiation; see the Role Assignment module for what each implies.
What the model does change is auditability. Because the rules are stored state rather than control flow, the permissions of a live component can be read off the ledger and compared against what its documentation claims, without trusting either the source code or the deployer.
External Links
- Radix Docs: Authorization & Access Rules
- Radix Docs: Access Rules & Roles
- radixdlt-scrypto:
proof_rule.rs(AccessRule and the requirement grammar) - radixdlt-scrypto:
auth_zone_substates.rs(what the auth zone holds) - radixdlt-scrypto:
auth_module.rs(call-frame auth zones and the global-context barrier) - radixdlt-scrypto: execution constants (rule depth and node caps)
