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 createdupdate
: Triggered after an existing document is updateddelete
: 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