CSR Volunteer Management Platform

Architecture Design Document

Client: John Keells Foundation, part of John Keells Holdings PLC
Prepared by: CloudNavision Private Limited
Author: Azmie Sally, Founder & CEO
Version: 3.0  |  Date: March 2026  |  Classification: Confidential











Document Control

Document Information

Approvals

Revision History

1. Executive Summary

This Architecture Design Document describes the technical architecture for the CSR Volunteer Management Platform deployed for John Keells Foundation, part of John Keells Holdings PLC. The platform enables enterprise-wide volunteer management, tracking, ratings, and impact reporting across all JKH business units.

The architecture is a fully managed, containerised deployment on Microsoft Azure with zero self-managed servers. Both the React web application and the n8n automation platform run as Azure Container Apps. All external traffic is routed exclusively through Cloudflare tunnels — no Azure resource has a public-facing origin endpoint. Outbound connectivity to Supabase is locked to a single fixed IP via NAT Gateway.

1.1 Key Architecture Decisions

Azure Container Apps

For both frontend (React/Express.js) and automation (n8n) — no VMs, no OS patching

Cloudflare Tunnel Model

Internal ingress only; both services use outbound tunnels, eliminating exposed origin endpoints

Express.js Server Layer

Within the React container, required to serve the built SPA from a Container App

Supabase SaaS

For managed PostgreSQL, authentication, edge functions, and Realtime

Microsoft SharePoint

For file storage via n8n and Microsoft Graph API

Cloudflare ZTNA

For n8n admin access only — web app uses WAF + Entra ID SSO (no double authentication for 15,000 users)

Microsoft Entra ID — Two Tenants

jkhmgmt.onmicrosoft.com for Azure subscription hosting only; keells.onmicrosoft.com for all users, app registrations, and employee SSO login

LinkedIn OAuth

For non-Entra ID users — secondary authentication

NAT Gateway

With static IP for fixed egress identity — required for Supabase network allowlist

SheetJS

For server-side Excel report generation within the Express.js layer

Revised Sizing

1.0 vCPU / 2.0 Gi per web app replica, min 2 / max 10 replicas, scaling on HTTP concurrent connections

2. Architecture Overview

2.1 High-Level Architecture Diagram

Figure 1: High-Level Architecture Diagram illustrating the five-layer deployment model — Users, Cloudflare Edge, Azure Container Apps, NAT Gateway, and External Services — for the CSR Volunteer Management Platform hosted on Azure (Southeast Asia / Singapore).

2.2 Architecture Layers

2.3 Subscription and Tenant Details

The two tenants serve entirely separate purposes. jkhmgmt.onmicrosoft.com is used solely for Azure subscription hosting — it contains no users and no app registrations. keells.onmicrosoft.com holds all JKH employees and is the tenant where all app registrations are created, including both the n8n automation registration and the Supabase Auth SSO registration. They do not interact.

2.4 Domain Names

All subdomains are exclusively under johnkeellsfoundation.com. DNS is managed by Cloudflare. The api subdomain is reserved — no DNS record should be created until required.

2.5 Naming Convention

All Azure resources follow the pattern: JKH-{ENV}-FND-{TYPE}-{INSTANCE}

Resource type abbreviations:

2.6 Core Principles

3. Network Design

3.1 Virtual Network

3.3 NAT Gateway

3.2 Subnet Allocation

3.4 Network Security Groups

JKH-PRD-FND-NSG-ACA — Container Apps subnet

Inbound DenyAll is the primary perimeter — both Container Apps have internal-only ingress so no legitimate inbound traffic arrives directly. All external traffic arrives via outbound Cloudflare tunnels.

JKH-PRD-FND-NSG-ACR — Container Registry subnet

3.5 Public-Facing URLs

3.6 Service Communication Map

4. Azure Resources

4.1 Azure Container Registry

4.2 Container Apps Environment

4.3 Container App — React Web App

App Configuration

Containers

Container App Secrets (never in code)

TUNNEL_TOKEN = <JKH-PRD-FND-TUNNEL-CSR token>

Build-time variables (GitHub Actions secrets — injected at docker build via --build-arg)

VITE_SUPABASE_URL = https://<ref>.supabase.co VITE_SUPABASE_ANON_KEY = <anon key> VITE_ENTRA_CLIENT_ID = <client ID — keells.onmicrosoft.com tenant> VITE_ENTRA_TENANT_ID = <tenant ID — keells.onmicrosoft.com> VITE_ENTRA_REDIRECT_URI = https://csr.johnkeellsfoundation.com/auth/callback VITE_LINKEDIN_CLIENT_ID = <LinkedIn OAuth client ID> VITE_N8N_WEBHOOK_BASE_URL = https://n8n.johnkeellsfoundation.com VITE_N8N_WEBHOOK_SECRET = <webhook auth token>

The AI translation service (Google Translate API) is stored as a Supabase Edge Function secret, not a frontend variable. It is not a build argument and does not appear in the Docker build.

4.4 Container App — n8n

App Configuration

Containers

Container App Secrets

TUNNEL_TOKEN = <JKH-PRD-FND-TUNNEL-N8N token> N8N_ENCRYPTION_KEY = <random 32-char string> DB_POSTGRESDB_PASSWORD = <Supabase DB password> SUPABASE_SERVICE_KEY = <Supabase service role key — never in frontend>

Environment Variables

N8N_HOST = n8n.johnkeellsfoundation.com WEBHOOK_URL = https://n8n.johnkeellsfoundation.com N8N_PROTOCOL = https N8N_PORT = 5678 DB_TYPE = postgresdb DB_POSTGRESDB_HOST = db.JKHFOUNDATION.supabase.co DB_POSTGRESDB_PORT = 5432 DB_POSTGRESDB_DATABASE = postgres DB_POSTGRESDB_SCHEMA = n8n_data DB_POSTGRESDB_USER = postgres EXECUTIONS_DATA_PRUNE = true EXECUTIONS_DATA_MAX_AGE = 336

5. Cloudflare Configuration

5.1 Tunnels

5.2 WAF — Applied to Both Hostnames

5.3 CDN Caching — volunteers.johnkeellsfoundation.com Only

Cloudflare CDN caches all static assets at the edge. For a React SPA, this means the vast majority of bytes transferred never reach the origin container. Effective concurrent connections at origin for 600 concurrent users is estimated at 120–180 (20–30% of total).

5.4 Access Protection Decision — Per Service

5.5 ZTNA Access Policy — n8n.johnkeellsfoundation.com Only

Identity provider: Microsoft Entra ID (keells.onmicrosoft.com)

6. Security Architecture

6.1 Security Model

6.2 Secrets Management

All credentials are managed through two secure stores. No secret is stored in source code, Docker images, or configuration files committed to GitHub.

6.3 Transport Security

6.4 Data Security

At Rest

Supabase encryption — AES-256 managed by Supabase

In Transit

TLS 1.2+ — All connections encrypted

Row-level

Supabase RLS — Per-table, per-user policies on all csr_app tables

Token

JWT — Stateless authentication via Supabase Auth tokens

7. Authentication Architecture

7.1 Overview — Two Separate Tenants

This platform involves two distinct Entra ID tenants serving entirely different purposes. They must never be confused:

Both app registrations — the n8n automation registration and the Supabase Auth SSO registration — are created in keells.onmicrosoft.com. The jkhmgmt.onmicrosoft.com tenant is used exclusively for the Azure subscription and contains no users or app registrations.

7.2 User Authentication Flow

7.3 Part A — n8n App Registration (Amendment)

A.1 Registration Details

A.2 Current Permission State (Granted)

A.3 Additional Permissions Required

Review items from original document: Contacts.Read vs Contacts.ReadWrite — recommend Contacts.Read only (least privilege). Mail.ReadWrite.Shared and Mail.Send.Shared — not required for current workflow scope; add only if workflows send from shared mailboxes.

A.4 Open Items — Status

7.4 Part B — Supabase Auth App Registration (New)

This is a new, separate app registration required to enable Microsoft Entra ID as the SSO identity provider for volunteer and employee login via Supabase Auth. It is entirely separate from the n8n registration but resides in the same keells.onmicrosoft.com tenant.

B.1 Registration Overview

This registration must be created in the keells.onmicrosoft.com tenant. JKH employees authenticate with their @keells.com Microsoft accounts, which live in the keells tenant. All app registrations for this platform reside in this tenant.

B.2 Required Permissions

B.3 SAP Employee ID — Entra ID Attribute Mapping

Feature B.19 specifies that the volunteer's SAP number is automatically populated from the Microsoft identity platform during sign-in. This requires the SAP employee ID to be stored as a custom or extension attribute on the user's Entra ID profile in keells.onmicrosoft.com.

Once confirmed, the Supabase Auth hook reads this attribute at login and populates the SAP Number field on the volunteer profile. It will not overwrite manual entries per B.19 specification.

B.4 Token Configuration

The following optional claims must be added to the token configuration in the Azure Portal:

In Azure Portal → App Registration → Token configuration → Add optional claim → ID token:

For the groups claim: Token configuration → Add groups claim → Security groups → ID token.

Group IDs are returned as GUIDs in the token. A mapping table in the platform (configured by Central Admin per SC.6) maps these GUIDs to Business Units. JKH IT must provide the Entra ID group GUIDs that correspond to each Business Unit.

B.5 Client Secret

The client secret is entered directly into the Supabase Auth provider configuration. It is never stored in code, GitHub, or any config file.

B.6 Supabase Auth Provider Configuration

Once the app registration is created, configure in Supabase Dashboard → Authentication → Providers → Azure:

B.7 Redirect URI Configuration

In Entra ID app registration → Authentication → Redirect URIs:

https://JKHFOUNDATION.supabase.co/auth/v1/callback

In Supabase Dashboard → Authentication → URL Configuration:

Site URL : https://volunteers.johnkeellsfoundation.com Redirect URLs : https://volunteers.johnkeellsfoundation.com/auth/callback https://volunteers.johnkeellsfoundation.com/**

7.5 LinkedIn OAuth (Secondary Authentication)

LinkedIn OAuth is provided as a secondary sign-in path for non-Entra ID users (e.g. external partners, contractors, or alumni who do not hold a @keells.com Microsoft account).

LinkedIn OAuth users do not receive SAP employee ID auto-population (B.19) or automatic Business Unit assignment (SC.6), as these rely on Entra ID group and profile attributes. Manual data entry applies.

8. Supabase Configuration

8.1 Supabase Specification

8.2 Database Schemas

8.3 Connection Summary

8.4 Network Allowlist

All Supabase access is either browser-side (protected by RLS and anon key scope) or via the NAT Gateway (Container Apps server-side). Supabase dashboard access for CloudNavision and JKH IT is always permitted by Supabase independently of network restrictions.

8.5 Auth Redirect URLs

Site URL : https://volunteers.johnkeellsfoundation.com Redirect URLs : https://volunteers.johnkeellsfoundation.com/auth/callback https://volunteers.johnkeellsfoundation.com/**

8.6 Row Level Security

RLS must be enabled and verified on all tables in the csr_app schema before go-live. The anon key used by the frontend grants zero access beyond what RLS policies explicitly permit.

8.7 Realtime — Connection Management

The React app uses Supabase Realtime for live in-app notifications (Feature B.15). Each active user maintains a persistent WebSocket connection. At 600 concurrent users this consumes 600 of the available 600 pooled connections — leaving no headroom for REST API calls and direct n8n PostgreSQL connections.

Realtime channel pattern — user-scoped subscriptions only:

//

9. n8n Configuration

9.1 n8n Specification

9.2 n8n Version Pinning and Upgrade Policy

9.3 n8n Workflows

Teams Notifications

Trigger: Webhook from Web App

Send event reminders, status updates, and alerts to Microsoft Teams

New Project Notification

Trigger: Webhook on project creation

Dispatch full project details to downstream systems

File Upload to SharePoint

Trigger: Webhook from Web App

Upload evaluation photos, documents, and event images to SharePoint

Teams Adaptive Card Responses

Trigger: Teams Webhook

Process user interactions with Teams Adaptive Cards

Report Generation

Trigger: Scheduled / On-demand

Generate advanced reports and save to SharePoint

10. Data Flow Diagrams

10.1 User Authentication Flow

01

Navigate

User navigates to volunteers.johnkeellsfoundation.com

02

Route

Cloudflare routes request to ca-jkh-csr-prd via Cloudflare tunnel

03

Sign-in Options

Web app presents sign-in options: Microsoft Entra ID (keells.onmicrosoft.com) or LinkedIn

04

Authenticate

User authenticates with selected provider (+ MFA if configured for Entra ID)

05

OAuth Tokens

Identity provider returns OAuth tokens to web app

06

Exchange

Web app exchanges tokens with Supabase Auth

07

SAP ID

On first Entra ID login: SAP employee ID auto-populated from Entra ID profile (B.19)

08

BU Mapping

On first Entra ID login: Group GUIDs mapped to Business Unit (SC.6)

09

Session

User session established with role-based access

10.2 Volunteer Registration Flow

01

Browse Projects

Volunteer browses available projects in web app

02

Fetch Data

Web app fetches project data from Supabase (REST API, anon key + RLS)

03

Submit Application

Volunteer submits application

04

Create Record

Web app creates application record in Supabase

05

Webhook

Web app sends webhook to n8n (n8n.johnkeellsfoundation.com/webhook/...)

06

Teams Notification

n8n sends Teams notification to volunteer and administrator via Microsoft Graph

07

Log Update

n8n updates notification log in Supabase (n8n_data schema)

10.3 File Upload Flow

01

Upload

User uploads photo or document via web app

02

Send to n8n

Web app sends file data to n8n webhook

03

Authenticate

n8n authenticates with Microsoft Graph API (using JKHMIT_FOUNDATION_n8nAuto_PRD credentials from keells.onmicrosoft.com)

04

Upload to SharePoint

n8n uploads file to SharePoint (John Keells Foundation library)

05

Return URL

n8n returns SharePoint URL to web app

06

Store Reference

Web app stores SharePoint URL reference in Supabase

10.4 Report Generation Flow

01

Request Report

Central Admin requests report via web app

02

Generate Excel

SheetJS generates Excel workbook within the Express.js server layer

03

Download

Report available for immediate download from the Reports module

04

Optional: SharePoint + Teams

Optionally: n8n uploads report to SharePoint and sends Teams notification with report link

11. Integration Details

11.1 Integration Summary

11.2 Microsoft Teams Integration

Teams integration is handled entirely through n8n workflows using the Microsoft Graph API:

Adaptive Cards

For project notifications and reminders

Channel Messages

For team announcements

Direct Messages

For individual notifications

Adaptive Card Actions

Action responses via webhooks

11.3 SharePoint Integration

SharePoint integration is handled through n8n workflows. The web app never connects directly to SharePoint. Flow: Web app sends file data to n8n webhook → n8n authenticates with SharePoint using JKHMIT_FOUNDATION_n8nAuto_PRD service principal (Sites.Selected permission) from keells.onmicrosoft.com → n8n uploads file to designated folder in John Keells Foundation library → n8n returns SharePoint URL to web app for storage in Supabase.

12. Monitoring and Logging

12.1 Azure Monitoring

Application Performance

Azure Application Insights (JKH-PRD-FND-APPI-001)

Request tracking, performance metrics, exceptions

Infrastructure Logs

Azure Log Analytics (JKH-PRD-FND-LOG-001)

Container logs, diagnostic data, custom queries

Alerts

Azure Monitor Alerts

Threshold-based alerting for critical metrics

12.2 Key Metrics

12.3 Log Retention

13. CI/CD Pipeline

13.1 Source Control

13.2 Pipeline Flow

git push → main │ ▼ GitHub Actions ├── npm ci ├── npm run build (Vite — VITE_ vars injected as build args) ├── docker build (two-stage — builder + serve) ├── docker push → acrjkhjkfprd.azurecr.io/jkh-csr-app:<sha> └── az containerapp update → ca-jkh-csr-prd (rolling redeploy)

n8n is not in the CI/CD pipeline — it uses the official n8nio/n8n image updated manually via a pinned version tag.

13.3 Dockerfile

# Stage 1 — Build FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . ARG VITE_SUPABASE_URL ARG VITE_SUPABASE_ANON_KEY ARG VITE_ENTRA_CLIENT_ID ARG VITE_ENTRA_TENANT_ID ARG VITE_ENTRA_REDIRECT_URI ARG VITE_LINKEDIN_CLIENT_ID ARG VITE_N8N_WEBHOOK_BASE_URL ARG VITE_N8N_WEBHOOK_SECRET RUN npm run build # Stage 2 — Serve FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev COPY server.js ./ COPY --from=builder /app/dist ./dist EXPOSE 8080 CMD ["node", "server.js"]

13.4 GitHub Actions Workflow

name: Build and Deploy on: push: branches: [main] env: ACR_NAME: acrjkhjkfprd IMAGE_NAME: jkh-csr-app CONTAINER_APP: ca-jkh-csr-prd RESOURCE_GROUP: JKH-PRD-FND-RG-001 jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Log in to Azure uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Log in to ACR run: az acr login --name $ACR_NAME - name: Build and push image run: | docker build \ --build-arg VITE_SUPABASE_URL=${{ secrets.VITE_SUPABASE_URL }} \ --build-arg VITE_SUPABASE_ANON_KEY=${{ secrets.VITE_SUPABASE_ANON_KEY }} \ --build-arg VITE_ENTRA_CLIENT_ID=${{ secrets.VITE_ENTRA_CLIENT_ID }} \ --build-arg VITE_ENTRA_TENANT_ID=${{ secrets.VITE_ENTRA_TENANT_ID }} \ --build-arg VITE_ENTRA_REDIRECT_URI=${{ secrets.VITE_ENTRA_REDIRECT_URI }} \ --build-arg VITE_LINKEDIN_CLIENT_ID=${{ secrets.VITE_LINKEDIN_CLIENT_ID }} \ --build-arg VITE_N8N_WEBHOOK_BASE_URL=${{ secrets.VITE_N8N_WEBHOOK_BASE_URL }} \ --build-arg VITE_N8N_WEBHOOK_SECRET=${{ secrets.VITE_N8N_WEBHOOK_SECRET }} \ -t $ACR_NAME.azurecr.io/$IMAGE_NAME:${{ github.sha }} \ -t $ACR_NAME.azurecr.io/$IMAGE_NAME:latest \ . docker push $ACR_NAME.azurecr.io/$IMAGE_NAME:${{ github.sha }} docker push $ACR_NAME.azurecr.io/$IMAGE_NAME:latest - name: Deploy to Container Apps run: | az containerapp update \ --name $CONTAINER_APP \ --resource-group $RESOURCE_GROUP \ --image $ACR_NAME.azurecr.io/$IMAGE_NAME:${{ github.sha }}

13.5 GitHub Actions Secrets Required

13.6 Environments

14. Scalability Plan

14.1 Load Profile

15K

Total User Base

All JKH employees — potential volunteers

600

Peak Concurrent (Baseline)

~4% of base — typical for web-based employee portal

1,500

Peak Concurrent (Headroom)

Revised sizing target

1

Realtime Connections

Per active user — Supabase Realtime for live notifications (B.15)

14.2 Cloudflare Edge Absorption

Cloudflare CDN caches all static assets at the edge. Only HTML requests and API calls reach the origin container.

Estimated effective concurrent connections at origin for 600 concurrent users: 120–180 (20–30% of total).

14.3 Container Sizing — Current (Revised)

Scaling rule (Container Apps HTTP scaling):

Scale metric : http_requests (concurrent) Target per replica: 150 Min replicas : 2 Max replicas : 10 Scale-out cooldown: 30 seconds Scale-in cooldown : 120 seconds

Minimum 2 replicas eliminates cold-start delay for the first users. Scale-in cooldown is 120s to prevent thrashing during sustained load.

14.4 Scale-Up Tiers

14.5 Supabase Compute — Connection Tiers

14.6 Scaling Triggers

14.7 Scaling Principles

  • Container Apps scale horizontally by adding replicas — no downtime, no architectural change required
  • Supabase compute upgrades are performed via the Supabase dashboard with minimal downtime (~2–3 minutes)
  • Cloudflare CDN has no per-connection limits relevant to this scale — no changes needed at any tier
  • n8n must remain at 1 replica at all tiers — it is not in the user-facing request path and must not scale horizontally
  • All scale-up actions require no code changes, no redeployment, and no architectural rework

14.8 Scaling Responsibilities

14.9 Load Testing Recommendation

Before go-live, a load test against the staging environment should validate the revised sizing.

Recommended tool: k6 (open source, integrates with Azure and GitHub Actions)

15. Backup and Recovery

15.1 Backup Configuration

Supabase Pro Plan includes automated daily backups with point-in-time recovery (PITR).

15.2 Recovery Procedure

15.3 Disaster Recovery — Platform Level

15.4 Backup Responsibilities

16. Provisioning Order

The following sequence must be followed exactly. Steps are ordered to satisfy dependencies — each step assumes all prior steps are complete.

01

Resource Group

JKH-PRD-FND-RG-001

02

Log Analytics

JKH-PRD-FND-LOG-001

03

VNet + all subnets

JKH-PRD-FND-VNET-001

04

NSGs

JKH-PRD-FND-NSG-ACA, JKH-PRD-FND-NSG-ACR

05

Associate NSGs

Attach each NSG to its subnet

06

Public IP (NAT)

JKH-PRD-FND-PIP-NAT — record static IP immediately

07

NAT Gateway

JKH-PRD-FND-NAT-001 → associate to JKH-PRD-FND-SNET-ACA

08

Container Registry

acrjkhjkfprd → private endpoint in JKH-PRD-FND-SNET-ACR

01

Supabase Network Restrict

Add JKH-PRD-FND-PIP-NAT + JKH office IPs to allowlist

02

Supabase Auth URLs

Update redirect URLs to johnkeellsfoundation.com; remove Lovable dev URLs

03

Container Apps Environment

cae-jkh-jkf-prd → JKH-PRD-FND-SNET-ACA

04

Cloudflare Tunnels

Create JKH-PRD-FND-TUNNEL-CSR + JKH-PRD-FND-TUNNEL-N8N → save both tokens

05

Cloudflare Access Policy

ZTNA for n8n.johnkeellsfoundation.com + webhook bypass rule

06

Cloudflare WAF Rules

Apply OWASP + bot rules to both hostnames

07

Container App (n8n)

ca-jkh-n8n-prd — n8n + cloudflared sidecar + secrets

08

Verify n8n Tunnel

Cloudflare dashboard → JKH-PRD-FND-TUNNEL-N8N → Healthy

09

Container App (web app)

ca-jkh-csr-prd — React + cloudflared sidecar + secrets

01

Verify Web App Tunnel

Cloudflare dashboard → JKH-PRD-FND-TUNNEL-CSR → Healthy

02

GitHub Actions Secrets

AZURE_CREDENTIALS + all VITE_ vars

03

First Deployment

Push to main → verify full pipeline runs

04

App Insights

JKH-PRD-FND-APPI-001 → linked to JKH-PRD-FND-LOG-001

05

Update Redirect URIs

Entra ID (keells tenant) → csr.johnkeellsfoundation.com

06

n8n M365 App Registration

Complete open items A.3–A.4 (JKH IT)

07

RLS Audit

Verify all csr_app tables have RLS enabled

08

Enable PITR

Supabase dashboard → enable PITR add-on before go-live

09

End-to-End Smoke Test

Full user journey across all integrations

17. Open Items Before Provisioning

18. Browser and Device Support

18.1 Supported Browsers

The platform is a Progressive Web App (PWA) built on React 19.

Internet Explorer is not supported. Chromium-based browsers are recommended for the best experience.

18.2 Supported Devices

18.3 PWA Installation

Add to Home Screen (iOS)

Safari — Share → Add to Home Screen

Add to Home Screen (Android)

Chrome — Install prompt or browser menu

Install on Desktop (Chrome/Edge)

Address bar install prompt

Offline Support

⚠️ Limited — read-only cached views only; active features require connectivity

Push Notifications

Supported on Android and desktop; limited on iOS (iOS 16.4+ required)

Camera Access (QR Scanning)

Required for attendance check-in via QR code

18.4 Network Requirements

19. Appendices

Appendix A: Technology Stack Summary

Appendix B: Key Dependencies

Core Framework

Backend and Server

Forms and Validation

UI Components and Styling

Data Visualization, Date/Time, QR Code and Media

Radix UI Primitives

Appendix C: Revised Monthly Cost Estimate

Microsoft Entra ID and SharePoint are included in existing JKH Microsoft 365 subscriptions. AI translation (Google Translate API) is usage-based and billed separately via Google Cloud. Cost increase versus v2.0 estimate is driven by web app container scaling headroom (max 10 replicas vs 3) required to support up to 1,500 concurrent users.

Appendix D: Entra ID App Registration Action Items

Appendix E: Contact Information

Document Supersession Notice

This document supersedes Architecture Design Document v2.0 (March 2026). All changes are recorded in the Revision History above. The architecture described herein reflects the final agreed deployment model incorporating the pre-deployment architecture review, capacity sizing analysis, and Entra ID app registration amendments (March 2026).

Document Version

3.0 — March 2026

Supersedes

Architecture Design Document v2.0 (March 2026)

Classification

Confidential

Prepared By

CloudNavision Private Limited