coding5 min read

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!

Kevin Liu profile picture

Kevin Liu

November 21, 2025

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