Skip to main content

Permission Rules

Cocoon's PermissionRegistry contract enforces argument-level permissions — constraints on the values of method arguments, not just whether a role can call a method. This page documents the rule format, the default role matrix, and how to manage rules.

note

Argument-level permissions are a key Cocoon differentiator. Most permissioning systems enforce function-level access ("Trader can call transfer"). Cocoon's PermissionRegistry enforces argument-level constraints ("Trader can call transfer, but only with amount ≤ $1,000,000").

Rule Format

A permission rule is a tuple of:

(role, method, argument, constraint_type, constraint_value)
FieldTypeDescription
rolestringThe user role this rule applies to: Trader, SeniorTrader, Compliance, Auditor, Regulator, Admin
methodstringThe JSON-RPC method name (e.g. token_transfer)
argumentstringThe argument name to constrain (e.g. amount)
constraint_typestringOne of: max_value, min_value, exact_value, blocked, allowed
constraint_valuestring (numeric)The constraint threshold, encoded as a decimal string

Constraint Types

TypeMeaningExample
max_valueArgument must be ≤ this valueTrader transfer ≤ $1M
min_valueArgument must be ≥ this valueMinimum subscription amount
exact_valueArgument must equal this valueToken address must be a specific contract
blockedThis role may not call this method at allAuditor cannot call token_transfer
allowedNo constraint; this role may call freelyAdmin is allowed all methods

Encoding Amounts

Amounts in permission rules are stored in the token's smallest unit (18 decimals for most ERC-20 tokens). Human-readable values:

Human-readableEncoded value
$1,0001000000000000000000000 (1,000 × 10^18)
$1,000,0001000000000000000000000000 (1,000,000 × 10^18)
$5,000,0005000000000000000000000000 (5,000,000 × 10^18)

The admin dashboard's permission editor handles this encoding automatically — enter amounts in human-readable form and the dashboard converts them.

Default Role Matrix

The following rules are deployed by default in contracts/deploy.sh. They reflect the intended demo permission structure for the MVP.

RoleMethodArgumentConstraint
Tradertoken_transferamount≤ $1,000,000
Tradertoken_batchTransferamounts[*]Each ≤ $1,000,000
SeniorTradertoken_transferamount≤ $5,000,000
SeniorTradertoken_batchTransferamounts[*]Each ≤ $5,000,000
Compliancetoken_freezeallowed
Compliancetoken_unfreezeallowed
ComplianceAll write methods except freezeblocked
AuditorAll write methodsblocked
RegulatorAll write methodsblocked
AdminAll methodsallowed (unrestricted)

Managing Rules

Via the Admin Dashboard

  1. Navigate to http://localhost:3000/permissions
  2. The rule table shows all active rules
  3. Click Add Rule to create a new constraint
  4. Click any rule row to edit the constraint value
  5. Toggle the Active switch to disable a rule without deleting it

Via the PermissionRegistry Contract

Rules are stored on-chain in the PermissionRegistry contract. The contract address is in deployments.json.

// Add a new rule
permissionRegistry.setRule(
"SeniorTrader", // role
"token_transfer", // method
"amount", // argument
RuleType.MAX_VALUE, // constraint type
5_000_000 * 10**18 // $5M in wei
);

// Remove a rule
permissionRegistry.removeRule("SeniorTrader", "token_transfer", "amount");

// Check if a call is permitted
bool permitted = permissionRegistry.isPermitted(
"Trader",
"token_transfer",
"amount",
2_000_000 * 10**18 // $2M — will return false for Trader
);

Via the Backend Proxy API

# List all active rules
GET http://localhost:8546/api/permissions

# Add a rule
POST http://localhost:8546/api/permissions
Content-Type: application/json

{
"role": "SeniorTrader",
"method": "token_transfer",
"argument": "amount",
"constraint_type": "max_value",
"constraint_value": "5000000000000000000000000"
}

# Delete a rule
DELETE http://localhost:8546/api/permissions/{rule_id}

Permission Enforcement

When a request arrives at the backend proxy:

  1. The caller's role is read from the validated session token.
  2. The proxy queries the PermissionRegistry for rules matching (role, method, argument).
  3. For each matching rule, the corresponding argument value from the request is compared against the constraint.
  4. If any rule is violated, the proxy returns JSON-RPC error -32001 TransferNotAllowed with a message describing the violated rule.
  5. The blocked request is logged to the audit database with status: blocked.

Error Response Format

{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32001,
"message": "Permission rule violated: Trader role allows token_transfer.amount ≤ 1000000000000000000000000. Requested: 2000000000000000000000000."
}
}

The message field is human-readable and includes the violated rule in plain language, making it suitable for direct display in investor-facing UIs.

Adding a Custom Rule

Scenario: A compliance team requires that no single investor can redeem more than $500,000 worth of MMF tokens in a single transaction.

  1. In the admin dashboard, navigate to Permissions → Add Rule.
  2. Set:
    • Role: Trader
    • Method: token_redeem
    • Argument: shares
    • Constraint: Max Value = $500,000 (the dashboard converts to 500000000000000000000000)
  3. Click Save. The rule is written to the PermissionRegistry contract and takes effect immediately.

Alternatively, via the contract:

permissionRegistry.setRule("Trader", "token_redeem", "shares", RuleType.MAX_VALUE, 500_000 * 10**18);

Security Notes

  • Rules are stored on-chain and cannot be silently modified — any change is a contract transaction and is auditable.
  • The backend proxy enforces rules before forwarding to the chain. This is a defense-in-depth measure; the chain itself does not enforce argument-level rules.
  • To enforce rules at the chain level (without relying on the proxy), the PermissionRegistry can be called directly from token transfer hooks — this is a planned enhancement.