This repository starts with a local-first Azure RAG scaffold:
- SvelteKit frontend for the user experience
- FastAPI backend for API and orchestration
- Bicep infrastructure for Azure dev and prod environments
- GitHub Actions for repeatable deployment
- Copy
.env.exampleto.envand adjust values if needed. - Install frontend dependencies:
cd frontend
npm install- Install backend dependencies:
cd backend
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt- Start both services with Docker Compose:
docker compose up --build- Or run them separately:
cd backend
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000cd frontend
npm run dev -- --host 0.0.0.0 --port 5173infra/main.bicepprovisions a dev or prod environment.- GitHub Actions deploy the infrastructure and build/publish the backend image.
- Separate GitHub environments should hold the SWA deployment token for dev and prod.
- Azure Container Registry for backend image builds
- Azure Container Apps for FastAPI backend and worker app
- Azure Static Web Apps for SvelteKit frontend
- Azure Key Vault with RBAC enabled
- Azure Cosmos DB (Mongo API account)
- Azure OpenAI account with chat and embedding deployments
- Azure Event Grid topic and subscription to worker webhook
The backend receives a managed identity and reads cloud values from Key Vault at runtime.
The infrastructure is split into modules under infra/modules:
core.bicep: identity, key vault, ACR, Cosmos DB, OpenAI, Event Grid topic, Container Apps environmentapps.bicep: API and worker container appseventing.bicep: Event Grid subscription wired to worker endpointaccess.bicep: RBAC role assignmentsfrontend.bicep: Static Web App
infra/main.bicep orchestrates these modules for both dev and prod.
POST /api/rag/index: index a document with embeddingPOST /api/rag/chat: query documents and generate an answer with Azure OpenAIPOST /api/events/publish: publish domain events to Event GridPOST /api/worker/eventgrid: Event Grid webhook endpoint for worker processing
For local testing, you can set EVENTGRID_TOPIC_ENDPOINT and EVENTGRID_TOPIC_KEY directly in .env.
RAG endpoints support tenant-aware filtering from the X-MS-CLIENT-PRINCIPAL header. If you set REQUIRE_TENANT_CONTEXT=true, calls without company claim context are rejected.
By default, indexing and event publishing require administrator or companyadmin role claims. You can change indexing behavior locally with REQUIRE_ADMIN_FOR_INDEX.
Policy behavior:
companyadmin: restricted to its owncompany_idclaim; cannot index or publish for another company.administrator: may operate across companies whenALLOW_ADMIN_CROSS_COMPANY=true.
Admin route:
GET /api/admin/tenant-context: requiresadministratorrole and returns parsed tenant/user/roles context.
At first database access, the backend attempts to create tenant indexes and a vector search index. In local or non-vector-capable environments, vector index bootstrap warnings are surfaced in /api/config.
Create GitHub Environments named dev and prod, then set these secrets in each environment:
AZURE_CLIENT_IDAZURE_TENANT_IDAZURE_SUBSCRIPTION_IDSWA_DEPLOYMENT_TOKEN
The deployment workflow selects dev or prod and deploys infrastructure plus backend and frontend artifacts to that instance.
Cosmos DB Mongo API cannot switch an existing account from provisioned throughput to serverless in place. This repo now deploys a new serverless account name and keeps the old account until you copy data and validate.
Push to main (or run workflow dispatch) so infra/main.bicep creates the new serverless Cosmos account.
Install backend dependencies locally first (includes pymongo):
cd backend
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
cd ..Run migration helper:
scripts/migrate-cosmos-serverless.sh devIf auto-detection of the old account fails:
scripts/migrate-cosmos-serverless.sh dev <oldAccountName> <newServerlessAccountName>scripts/check-deploy.sh devThis confirms health, Key Vault secret resolution, and Cosmos round-trip behavior against the new connection string.
List Mongo accounts:
az cosmosdb list -g rg-azure-rag-dev --query "[?kind=='MongoDB'].name" -o tsvDelete the old provisioned account only after successful validation:
az cosmosdb delete -g rg-azure-rag-dev -n <oldProvisionedAccountName> --yesfrontendSkuNamedefaults toStandardin Bicep to support SWA linked backends.- SWA is linked to the API Container App resource so frontend requests can use
/api/*on one domain. frontend/staticwebapp.config.jsonincludes route protection and Entra login redirect behavior.
This project uses Azure Static Web Apps built-in authentication with Microsoft Entra ID. It is SSO-ready by default and does not require NextAuth/Auth.js for the current architecture.
- Minimal moving parts: SWA handles sign-in, session, and identity headers.
- Backend already parses
X-MS-CLIENT-PRINCIPALfor roles and tenant context. - Future SSO support remains straightforward through Entra tenant settings.
- Keep the SWA auth config in
frontend/staticwebapp.config.jsonas-is. - In Microsoft Entra, use a single-tenant app configuration for initial testing.
- Assign yourself the app roles needed by backend policies (
administratororcompanyadmin) if required. - Enable MFA for your user and prefer Microsoft Authenticator TOTP.
- MFA through Entra (Authenticator OTP challenge) is the simplest low-cost path.
- Standalone custom OTP/passwordless flows are intentionally out of scope here.
- Open the frontend and click Login with Entra.
- Complete sign-in and MFA challenge.
- Verify the auth panel on the home page (authenticated state, provider, roles).
- Call
GET /api/meand confirm principal claims are present. - Validate role-gated endpoint behavior:
GET /api/admin/tenant-contextreturns200for admin role.- The same endpoint returns
403for non-admin users.
The backend supports just-in-time (JIT) user provisioning with a default app role.
- First successful login (or first authenticated API request) automatically creates a user profile in Cosmos DB.
- New users receive the default app role from
DEFAULT_APP_ROLE(defaults tomember). - Effective authorization roles are the union of:
- Entra/SWA principal roles (
claims.rolesanduserRoles) - Stored app roles from the user profile
- Implicit
authenticatedrole for signed-in users
- Entra/SWA principal roles (
This means a separate registration page is not required.
Administrators can override app roles for any user:
PUT /api/admin/users/{user_id}/rolesGET /api/admin/users?q=<search>&limit=8for admin user lookup/autocomplete- Requires
administratorrole - Request body:
{
"roles": ["member", "companyadmin"],
"company_id": "optional-company-id"
}If roles is empty, the backend applies the configured default app role.