A Next.js chat application that combines FusionAuth for authentication, Permify for fine-grained authorization (FGA), and a RAG pipeline powered by Voyage AI (embeddings) and Anthropic Claude (generation). Users can only query documents they have permission to access -- permissions are enforced before documents reach the LLM, so unauthorized content is never exposed.
# Start infrastructure (FusionAuth, Permify, Ollama, PostgreSQL)
docker compose up -d
# Install dependencies
npm install
# Copy environment file (defaults work out of the box with kickstart)
cp .env.example .env
# Start the dev server
npm run dev
# Seed Permify schema, relationships, and sample documents
npm run seed:allOpen http://localhost:3000. Sign in with one of the seed users or create a new account:
| User | Password | Org Role | |
|---|---|---|---|
| Admin | admin@example.com |
password |
admin |
| Jane | jane@example.com |
password |
member |
| John | john@example.com |
password |
-- |
| Stranger | stranger@example.com |
password |
-- |
New users who register through FusionAuth are automatically added as members of the organization.
This project is a Next.js application that enforces authorization before RAG context reaches the LLM.
- Web app (Next.js App Router): UI pages and API routes (
/api/chat,/api/documents,/api/upload, org/team management). - Authentication (FusionAuth + NextAuth): OAuth login, token/session handling, and first-login organization membership bootstrap.
- Authorization (Permify): Relationship-based access checks (
view,member,admin, team/document relationships). - RAG Pipeline (Retriever + AI Client): Embedding generation, similarity search, permission filtering, and answer generation.
- Document Store:
- In-memory (default, non-durable)
- PostgreSQL via
DOCUMENTS_DATABASE_URL(durable)
- LLM/Embedding Providers:
- Voyage AI for embeddings
- Anthropic Claude for final answer generation
- User authenticates through FusionAuth (via NextAuth provider).
- User sends a chat query to
/api/chat. - Retriever embeds the query, ranks candidate documents by similarity, then asks Permify which documents the user can
view. - Only permitted documents are passed as context to Claude.
- API returns answer and source document IDs.
This "authorize before generate" model is the key security property of the system.
| Method | Endpoint | Description |
|---|---|---|
POST |
/api/chat |
Send a query -- returns an answer generated from permitted documents |
GET |
/api/chat/history |
List the current user's conversations |
GET |
/api/chat/history/[id] |
Get a specific conversation with messages |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/documents |
List all documents the current user can access |
POST |
/api/upload |
Upload a new document (auto-linked to the organization) |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/organization |
Get organization details and member list |
POST |
/api/organization/members |
Add a member by email (admin only) |
DELETE |
/api/organization/members |
Remove a member (admin only) |
POST |
/api/organization/admins |
Promote a member to admin (admin only) |
DELETE |
/api/organization/admins |
Demote an admin (admin only) |
| Method | Endpoint | Description |
|---|---|---|
* |
/api/auth/[...nextauth] |
NextAuth OAuth callbacks (FusionAuth provider) |
GET |
/api/logout |
Redirect to FusionAuth logout |
By default, uploaded documents are stored in memory only. For durable storage (required in Railway or any multi-instance deployment), set:
DOCUMENTS_DATABASE_URL=postgres://<user>:<password>@<host>:<port>/<database>When DOCUMENTS_DATABASE_URL is set, documents and embeddings are stored in PostgreSQL and survive restarts/redeploys.
- FusionAuth -- Authentication and user management
- Permify -- Fine-grained authorization (ReBAC)
