Skip to main content

Policies

Overview

Policies in Tarobase are the backbone of your application's security and data integrity. They define the rules for data access, storage, and manipulation within your app. By carefully crafting policies, you can control who can read or write data, enforce data structures, decide whether data is stored on-chain or off-chain, and automate actions based on data changes.

This page provides an in-depth understanding of how policies work in Tarobase and offers best practices for creating secure, scalable policies for your applications. For a quick reference while writing your policies, visit our Policy Cheatsheet.

Understanding Policies

What Are Policies?

A policy in Tarobase is a JSON object that defines rules and configurations for a specific path or set of documents within your application's data structure. Policies allow you to:

  • Control Access: Define who can read or write to specific parts of your data
  • Enforce Data Structure: Specify the expected fields and their data types
  • Manage Storage Location: Decide whether data should be stored on-chain or off-chain
  • Automate Actions: Trigger automatic actions (like token transfers) when certain conditions are met

Policy Structure

A typical policy consists of:

  • Path Definitions: The location in your data hierarchy that the policy applies to
  • Rules: Conditions under which read and write operations are permitted
  • Fields: Definitions of data fields and their types (required for on-chain storage)
  • On-Chain Storage Flag: Indicates whether the data should be stored on-chain
  • Triggers: Actions that occur after a successful write operation
  • Plugins: Extensions that provide additional functionalities within policies

Basic Policy Structure Example:

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

Writing Effective Policies

Defining Paths with Dynamic Segments

Use dynamic segments (prefixed with $) to create flexible and reusable policies that apply to multiple documents or collections.

Example:

{
"users/$userId": { ... },
"posts/$postId/comments/$commentId": { ... }
}

In this example, $userId, $postId, and $commentId are placeholders that match any value at that position in the path.

Rules: Controlling Access

Rules determine whether a read or write operation is permitted based on specified conditions.

  • Read Rule ("read"): Must evaluate to true for a read operation to succeed
  • Write Rule ("write"): Must evaluate to true for a write operation to succeed

Special Variables Available in Rules:

  • @walletAddress: The wallet address of the user making the request
  • @prevState: The current state of the document before the write operation
  • @newState: The proposed state of the document after the write operation
  • @root: Reference to the root of your data structure, allowing access to any part of the data hierarchy

Operators:

Logical Operators:

  • && (and)
  • || (or)
  • ! (not)

Comparison Operators:

  • ==, !=
  • <, >
  • <=, >=

Null Checks:

  • variable != null
  • variable == null

Example Rule:

"write": "@prevState == null && @newState.createdBy == @walletAddress"

This rule allows a write operation only if:

  • The document does not already exist (@prevState == null)
  • The createdBy field in the new state matches the user's wallet address

Fields: Enforcing Data Structure

When storing data on-chain, you must define the fields and their data types explicitly.

Supported Data Types:

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

Example Fields Definition:

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

On-Chain Storage

To specify that data should be stored on-chain, set the "onchain" property to true. This will deploy or update a smart contract on the blockchain to handle the data storage.

Example:

"onchain": true

Important Notes

  • All fields to be stored on-chain must be defined in the "fields" section
  • On-chain storage is ideal for data that requires immutability and transparency

Triggers: Automating Actions

Triggers allow you to automate actions after a write operation passes the rules.

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 Trigger:

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

This trigger transfers 1 USDC from the user's wallet to a specified recipient when a new document is created.

Plugins: Extending Functionality

Plugins provide additional functionality that can be used within policies. The @TokenPlugin is a built-in plugin for token-related operations.

TokenPlugin Actions:

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

Example Usage:

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

Best Practices for Secure and Scalable Policies

Principle of Least Privilege

  • Start with the most restrictive permissions and only grant additional access as needed
  • This minimizes potential security risks

Use Dynamic Segments Wisely

  • Leverage dynamic segments ($variableName) to generalize policies for similar data structures
  • Ensure that conditions within rules properly handle these dynamic segments

Explicitly Define Fields for On-Chain Data

  • Always specify fields and their data types when using on-chain storage
  • This ensures that your smart contract accurately reflects your data schema

Validate Data Integrity

  • Use @prevState and @newState to enforce data integrity rules
  • For example, prevent fields from being modified after creation or ensure certain fields are not null

Consider On-Chain Costs

  • Be mindful of the costs associated with on-chain storage and operations
  • Only store essential data on-chain and keep triggers efficient

Test Policies Thoroughly

  • Use Tarobase's simulation tools to test policies before deploying
  • Check that your policies enforce the intended access controls and data validations

Document Your Policies

  • Maintain clear documentation or comments within your policies
  • This aids in future maintenance and helps team members understand policy logic

Limit Triggers to Essential Actions

  • Overusing triggers can lead to complex policies and potential performance issues
  • Only include triggers that are necessary for your application's functionality

Use @root for Cross-Reference Checks

  • When you need to reference data outside the current document, use @root
  • Be cautious to avoid circular dependencies or performance bottlenecks