Access management: roles and domains
Authority throughout the protocol is governed by one model. No contract implements its own owner and its own role checks. Every privileged function is gated through an access manager that holds the roles and the policies that decide who may call it. Deployment rights, upgrade rights, agent powers, the administrative roles that govern trust: all are roles in this model.
This page describes how the model works, how roles are scoped, how a token suite is wired to it, and who sits at the top of each hierarchy. What each role gates is listed in Actors and roles.
The model
The protocol uses OpenZeppelin's AccessManager. A managed contract asks one question before it executes a privileged function: may this caller call this function on me? The manager answers from two tables.
Functions to roles
For each managed contract and each privileged function, the role that may call it.
Roles to members
For each role, the accounts that hold it, and for each the delay that applies to their calls.
Consolidating authority this way has two virtues.
One place to audit. Who can do what, across a whole token suite, is read from one contract. There is no patchwork of per-contract owners.
Authority can be delegated precisely. A role can be held by a single key, a multisig, an institution's governance process or an automated system, without changing the contracts that check it. How a role is held is a policy decision outside the contract.
The managers
Authority is not concentrated in one manager for everything. Each Issuer has its own manager, and the network has one for the contracts every asset shares.
The manager of a token suite, a TREXAccessManager
The Token, the TREXRegistry, the IdentityRegistryStorage and the ModularCompliance of one or several assets
The Issuer
The manager of the network
The TREXFactory, the TREXImplementationAuthority, the TrustedGatewayRegistry, the IdentityFactory, the ReputationRegistry, and the shared compliance modules and adapters
Network governance
The authority of a compliance module an Issuer deployed itself
The upgrade of that module
That Issuer
The roles of an Issuer's own manager have no reach over the network contracts, and the network's roles have no reach inside an Issuer's manager.
The network uses one manager, and not one holder. Its roles are different roles, held by different multisigs with their own members and thresholds: publishing versions, vetting gateways, administering the identity layer and setting reputation are separate powers in separate hands.
Roles are scoped to a domain
Several assets can share one manager. If roles were global to the manager, the minter of one asset could mint another. Roles are therefore scoped.
A domain is an issuer, or a fund: one team across every token in it. A role identifier is made of two parts, the domain and the role.
Domain, upper half of the identifier
Which group of assets the role applies to
Role number, lower half
Which role: OWNER, AGENT_MINTER, and so on
Two tokens in one domain share their agents. Granting
AGENT_MINTERin a domain lets its holder mint every token of that domain.Two domains are isolated. A role of one domain satisfies nothing in another, even in the same manager.
No role exists without a domain. A role identifier cannot be built for domain zero, so no unscoped role can be granted by omission.
An identifier can be read back. Tooling recovers the domain and the role from the identifier found in a grant event.
An Issuer can also define roles of its own in its domain, by name, next to the standard ones. They are mapped to functions and granted like any role.
The platform domain
Platform roles live in a reserved domain that no suite role can be built in: OWNER of the factory, VERSION_MANAGER, ASSET_DEPLOYER, INTEROP_MANAGER. Network governance holds OWNER, VERSION_MANAGER and INTEROP_MANAGER. It grants ASSET_DEPLOYER to the Issuers and tokenization providers that deploy suites. ASSET_DEPLOYER gates the deployment of a suite and the creation of the identity of a token, and nothing else: the references of the factory stay under OWNER.
The domain registry
The TREXAccessManager keeps a record of domains: createDomain(name) creates one, assign(domainId, target) records the domain of a token or of a storage, and domainOf, domainName and domainCount read it back.
This record is a directory. It is not what authorizes a call. Authorization is the role mapped to each function and the grants behind it. Commissioning keeps the two in step.
The hierarchy of a suite
An arrow means "grants and revokes". The roles below ADMIN_ROLE are each in a domain.
ADMIN_ROLE
OWNER, SUITE_ADMIN, AGENT_ADMIN. Maps functions to roles. Commissions suites.
The Issuer's governance
SUITE_ADMIN
The configuration roles of the suite
The Issuer's product or operations lead
AGENT_ADMIN
The agent roles, the keeper, the binding of storages
The Issuer's head of operations, or its transfer agent
IRS_WRITER is held by registries only and stays under ADMIN_ROLE. No administrator of a domain can hand it out. Agents edit investor records through a registry, never in the storage directly, so a storage shared between two domains gives the agents of one nothing over the other.
Trust statement. The holder of
ADMIN_ROLEon a manager can remap any function to any role and grant any role. It controls every suite that manager governs. It is the key an Issuer protects most.
Commissioning a suite
A deployed suite is not operable until its functions are mapped to roles: until then, every privileged function answers to the holder of ADMIN_ROLE only. This wiring is called commissioning.
Maps each privileged function of the token, the registry, the compliance and the storage to its role, in the suite's domain
The tables listed in Actors and roles
Sets which role administers which
The hierarchy above
Grants AGENT to the token
So that a recovery can register the new wallet
Grants IRS_WRITER to the registry, and binds the registry to its storage
So that registrations can be written
Commissioning requires ADMIN_ROLE. It can be run again: it applies the same standard tables.
There are two ways to get a commissioned suite.
At deployment
The factory deploys a TREXAccessManager, creates a domain named after the token, assigns the token and its storage to it, and commissions the suite.
The factory deploys the suite against the Issuer's manager and writes nothing into it.
Afterward
The factory hands ADMIN_ROLE to the account the Issuer named and renounces its own.
The Issuer commissions the suite itself. Until it does, the suite is not operable.
Suits
An Issuer's first asset
An Issuer that already has a manager and adds an asset to it, in an existing domain or a new one
The factory keeps no authority over a suite it has deployed.
An Issuer that wants a manager of its own can obtain it from the access manager factory of the T-REX Network [[TBC: the access manager factory]], which deploys the network's reference TREXAccessManager code. An Issuer can also deploy a manager by other means, and then answers for its code. In both cases the manager is the Issuer's: who holds its root, how roles are split and to whom they are granted are the Issuer's decisions and its responsibility. The network has no say in them.
Several assets, one manager
An Issuer with several assets chooses how to group them.
Several tokens in one domain
One team operates them all. A role granted once applies to every token of the domain.
Several domains in one manager
Separate teams per fund or per product line, under one root.
One manager per token
Complete separation, including of the root.
Tokens can be moved from one domain to others later. The operation remaps their functions to the new domain's roles, grants the new roles and revokes the old ones in one step.
Delays and scheduled operations
A role can be granted with an execution delay. Its holder then does not call the function directly. It schedules the call, waits for the delay, and executes it. During the delay, the operation is visible on-chain and can be cancelled by the account that scheduled it, by the holder of the guardian role that the manager attaches to that role, or by the admin of the manager. This guardian role belongs to OpenZeppelin's AccessManager and has nothing to do with the Guardians of an identity.
This turns any sensitive function into a timelocked one by configuration, without a change to the contract: the pause can be immediate while a change of compliance module waits 48 hours.
One detail: a batch function is authorized by the role of its single operation. A holder whose role carries a delay uses the single operation, scheduled. The batch form requires immediate permission. batchRegisterIdentity is the exception: it has its own mapping and can be scheduled.
owner() and the manager
Every suite contract exposes owner(), for tooling that expects one. It returns the address of the manager, not of a person. The manager of a suite is not replaced: it is an upgradeable contract that is upgraded in place, and the identity of the token is managed by it.
The token's identity follows the same roles
The ONCHAINID of a token has one MANAGEMENT key: the manager of its suite. Whoever the Issuer authorizes in the manager acts on the token's identity through it. The token and its identity are governed by the same structure.
The identity layer
The IdentityFactory and the ReputationRegistry are governed through the manager of the network, with the same mechanism, by roles that are distinct from the platform roles.
Identity layer administration
The policy and the modules of each identity type, the trusted gateways and verifiers
A creation role per identity type
Deploying an identity of that type for another account
The beacon upgrader, with a delay
Upgrading the implementation of every identity
REPUTATION_MANAGER
Reputation scores and the claim-add threshold
An ONCHAINID itself is not governed by a manager. It is governed by its own keys. See Keys, purposes and signers.
Reading the configuration
Everything about who can do what is on-chain and readable.
Which role gates this function?
getTargetFunctionRole(contract, selector) on the manager
Does this account hold this role, and with what delay?
hasRole(roleId, account)
Who administers this role?
getRoleAdmin(roleId)
Which domain is this token in?
domainOf(token)
Who is the authority of this contract?
authority() on the contract
Last updated