Skip to main content

Policy Cheatsheet

Overview

Welcome to the Policy Cheatsheet!

This quick reference guide is designed to assist you in writing effective policies for your Tarobase app. For a deeper understanding of how policies work and best practices for creating secure and scalable policies, please visit the Policies page.

Policy Structure

A Tarobase policy is a JSON object that defines rules, fields, storage preferences, and triggers for specific paths in your data structure.

Basic Structure:

{
"path": {
"rules": {
"read": "<condition>",
"write": "<condition>"
},
"fields": {
"fieldName": "Types.DataType"
},
"onchain": true | false,
"triggers": [
"<operation>: <action>"
]
}
}

Example:

{
"messages/$messageId": {
"rules": {
"read": "true",
"write": "@prevState == null && @newState.text != null && @newState.createdBy == @walletAddress"
},
"fields": {
"createdBy": "Types.Address",
"text": "Types.String"
},
"onchain": true,
"triggers": [
"create: @TokenPlugin.transferWholeTokens(@TokenPlugin.USDC, @walletAddress, recipientAddress, 1)"
]
}
}

Special Variables

  • @prevState: The current state of the document before the write operation.
  • @newState: The proposed new state of the document during the write operation.
  • @walletAddress: The wallet address of the user making the request.
  • @root: References the root of your app's document structure, allowing you to access any document.

Conditions and Operators

Use standard JavaScript-like operators to define conditions:

  • Logical Operators: && (and), || (or), ! (not)
  • Comparison Operators: ==, !=, <, >, <=, >=
  • Null Check: variable != null, variable == null

Rules

Define who can read and write to specific paths:

  • Read Rule ("read"): Condition that must be true for a read operation to succeed
  • Write Rule ("write"): Condition that must be true for a write operation to succeed

Examples:

  • Allow everyone to read:
"read": true
  • Allow writes only if the user is the owner:
"write": "@newState.createdBy == @walletAddress"

Fields and Data Types

Specify the expected fields and their data types for on-chain storage:

Available Data Types:

  • Types.String
  • Types.Address
  • Types.Int
  • Types.UInt
  • Types.Bool

Example:

"fields": {
"createdBy": "Types.Address",
"text": "Types.String",
"amount": "Types.UInt"
}

On-Chain Storage

To store data on-chain, set the "onchain" property to true:

"onchain": true

Note: When using on-chain storage, you must define all fields you will use in the "fields" section.

Triggers

Triggers allow you to perform additional actions after a successful write operation.

Syntax:

"triggers": [
"<operation>: <action>"
]

Supported Operations:

  • create: Triggered after a new document is created
  • update: Triggered after an existing document is updated
  • delete: Triggered after a document is deleted

Example:

"triggers": [
"create: @TokenPlugin.transferWholeTokens(@TokenPlugin.USDC, @walletAddress, recipientAddress, amount)"
]

Plugins

Plugins extend the functionality of your policies. For example, the @TokenPlugin provides token-related actions.

TokenPlugin Actions:

  • @TokenPlugin.transferWholeTokens(tokenAddress, fromAddress, toAddress, amount)
  • @TokenPlugin.transfer(tokenAddress, fromAddress, toAddress, amountInWei)

Example Usage in Triggers:

"triggers": [
"create: @TokenPlugin.transferWholeTokens(@TokenPlugin.USDC, @walletAddress, 0xRecipientAddress, 1)"
]

Sample policy with nested paths:

{
"posts/$postId/comments/$commentId": {
"rules": {
"read": "@root/posts/$postId.createdBy == @walletAddress || @prevState.createdBy == @walletAddress",
"write": "@prevState == null && @newState.createdBy == @walletAddress"
},
"fields": {
"text": "Types.String",
"createdBy": "Types.Address"
}
}
}

Explanation:

Explanation:

  • Read Rule: Allows reading if the user is the post owner or the comment creator.
  • Write Rule: Allows creating a new comment if the user is the one making the request.
  • Fields: Defines text and createdBy fields for on-chain storage.

Best Practices

  1. Principle of Least Privilege: Start with the minimal permissions necessary and expand as needed.

  2. Use Dynamic Segments: Leverage $variable in paths to make policies reusable.

  3. Explicit Field Definitions: Always define fields and data types when using on-chain storage.

  4. Validate Data: Use conditions in write rules to ensure data integrity.

  5. Test Policies Thoroughly: Before deploying, test your policies to ensure they behave as expected.

  6. Limit Triggers: Use triggers judiciously to avoid unnecessary complexity and potential performance issues.

  7. Document Your Policies: Add comments within your JSON (if supported) or maintain separate documentation to explain complex rules.

Quick Reference

Allow Read to Everyone:

"read": "true"

Allow Write Only to Owner:

"write": "@newState.createdBy == @walletAddress"

Check for Non-Null Fields:

"@newState.text != null"

Prevent Updates or Deletes:

"write": "@prevState == null"

Use Triggers for Actions:

"triggers": [
"create: <action>"
]

Common Use Cases

User Authentication:

Allow users to read and write their own data:

"users/$userId": {
"rules": {
"read": "@walletAddress == $userId",
"write": "@walletAddress == $userId"
}
}

Public Data with Controlled Writes:

Allow everyone to read but restrict who can write:

"publicData/$id": {
"rules": {
"read": "true",
"write": "@walletAddress == 0xAdminAddress"
}
}

Feel free to refer back to this cheatsheet whenever you're crafting or updating your Tarobase policies. If you have any questions or need further assistance, don't hesitate to reach out in our discord community.