Build a Multi-Tenant RAG with Fine-Grain Authorization
Discover how to build a multi-tenant RAG system with fine-grained authorization, inspired by Stardew Valley. Perfect for developers looking to enhance their applications!
Introduction
Are you a coding enthusiast who enjoys immersive simulation games? You might see similarities between managing a farm in Stardew Valley and developing robust applications. Like managing crops, seasons, and resources in the game, developers handle data, permissions, and user access. This blog post will guide you through building a Retrieval Augmented Generation (RAG) system with fine-grained authorization using Motia and SpiceDB, drawing inspiration from Stardew Valley's agricultural mechanics.
What Is RAG?
Retrieval Augmented Generation (RAG) enhances language model responses by combining external data retrieval with language generation models. This method improves the accuracy and relevance of responses from models like OpenAI by using specific datasets. We'll create a harvest logbook that lets users securely and efficiently query farm data.
Why Does Fine-Grained Authorization Matter?
In a multi-tenant application, it's crucial that users access only their data. Fine-grained authorization protects sensitive information. SpiceDB offers a powerful way to implement relationship-based access control (ReBAC), enabling us to set permissions at a detailed level.
Getting Started
Before you start coding, make sure you have:
- An OpenAI API key for embeddings and chat
- A Pinecone account with an index created (1536 dimensions, cosine metric)
- Docker for running SpiceDB locally
Step 1: Setting Up the Project
To create a new Motia project, run:
npx motia@latest create
Choose the Base template and name your project harvest-logbook-rag. Then, move into your project directory with:
cd harvest-logbook-rag
Next, install the SpiceDB client for authorization:
npm install @authzed/authzed-node
Step 2: Configuring Pinecone
Create a Pinecone project and set up your index with these settings:
- Name: harvest-logbook
- Dimensions: 1536
- Metric: cosine
Remember to save your API key and host information.
Step 3: Running SpiceDB
To launch SpiceDB locally, use:
docker run -d \
--name spicedb \
-p 50051:50051 \
authzed/spicedb serve \
--grpc-preshared-key "sometoken"
Check if SpiceDB is running with:
docker ps | grep spicedb
You should see it listed as active.
Step 4: Defining the Authorization Schema
Create a SpiceDB schema file in your project. This schema defines the relationships and permissions for users, organizations, and farms:
# spicedb.schema
definition user {}
definition organization {
relation admin: user
relation member: user
permission view = admin + member
permission edit = admin + member
permission query = admin + member
permission manage = admin
}
definition farm {
relation organization: organization
relation owner: user
relation editor: user
relation viewer: user
permission view = viewer + editor + owner + organization->view
permission edit = editor + owner + organization->edit
permission query = viewer + editor + owner + organization->query
permission manage = owner + organization->admin
}
This schema outlines the application's roles and their associated permissions.
Step 5: Building the Workflow
In Motia, we'll create an event-driven architecture to manage harvest data and queries. Each component will be a single file in the steps/ directory:
- ReceiveHarvestData: Stores harvest entries.
- ProcessEmbeddings: Manages text chunking and embedding generation.
- QueryAgent: Executes RAG queries against the harvest data.
- LogToSheets: Logs queries and responses for auditing.
Step 6: Implementing the API
Set up an API endpoint to collect harvest log data. Validate the input and trigger further processing:
import { z } from 'zod';
const bodySchema = z.object({
content: z.string().min(1, 'Content cannot be empty'),
farmId: z.string().min(1, 'Farm ID is required'),
metadata: z.record(z.any()).optional(),
query: z.string().optional()
});
export const config = {
type: 'api',
path: '/harvest_logbook',
method: 'POST',
emits: ['process-embeddings', 'query-agent'],
bodySchema
};
This setup ensures all necessary fields are present before proceeding.
Step 7: Handling Text Processing
Improve retrieval accuracy by splitting text into chunks and generating embeddings with OpenAI. Store these embeddings in Pinecone for semantic search:
const vectorIds = await HarvestLogbookService.storeEntry({
id: entryId,
content,
metadata,
timestamp: new Date().toISOString()
});
This process allows efficient search through harvest data based on user queries.
Step 8: Querying the Data
Create a separate endpoint for querying the harvest data. This keeps the API clean and supports different permission models:
export const config = {
type: 'api',
path: '/harvest_logbook/query',
method: 'POST',
emits: ['query-agent']
};
Users can now query and receive relevant answers based on their permissions.
Step 9: Logging Queries
Log every query and its AI-generated response to create an audit trail. This provides valuable insights into user interactions:
await HarvestLogbookService.logToSheets(query, response, sources);
Configure logging to either CSV files or Google Sheets, depending on your setup.
Conclusion
Creating a multi-tenant RAG system with fine-grained authorization using Motia and SpiceDB not only improves user experience but also secures data. By adopting an event-driven architecture, you can effectively manage complex authorization scenarios. As you dive into coding, balance user experience with data protection, mirroring the careful management of a Stardew Valley farm!
For more resources and the complete code, check out the Motia GitHub repository. Don't hesitate to reach out with questions or join our community discussions for support. Happy coding!
Related Articles
Top Highlights from Git 2.52: New Features for Developers
Explore the key features and enhancements in Git 2.52, including improved performance, new functionalities, and user experience upgrades for developers.
Nov 22, 2025
Should We Even Have :closed? Exploring CSS State Management
Explore the debate around the CSS pseudo-class :closed. Is it necessary or does :not(:open) suffice? Dive into coding insights and best practices.
Nov 21, 2025
Introducing Business Directory Script: Build Modern Directory Websites
Explore the Business Directory Script, designed to simplify creating directory websites, saving time while enhancing functionality and user experience.
Nov 21, 2025
