Live App →

Sentinel Frontend — Architecture

Next.js 16 App Router application serving two financial intelligence services: Nexus (Document Intelligence) and Zen (Financial AI Chat).

Live: https://sentinel-dev.centricitywealth.tech Branch: uat


Tech Stack

Layer Technology Version
Framework Next.js (App Router) 16.1.1
Runtime React 19.2.3
Language TypeScript ^5
Styling Tailwind CSS ^4
State Zustand ^5.0.10
Data Fetching TanStack React Query ^5.90.16
Animation Framer Motion ^12.27.1
Validation Zod ^4.3.5
Toasts Sonner ^2.0.7
Icons Lucide React ^0.562.0
E2E Testing Playwright (5 browser projects)

Route Architecture

app/
├── page.tsx                          # / — Landing (public)
├── (auth)/
│   ├── layout.tsx                    # Server: redirects authenticated → /dashboard
│   ├── login/page.tsx                # /login
│   └── register/page.tsx             # /register
└── (dashboard)/
    ├── layout.tsx                    # Server: verifies session, hydrates user
    ├── page.tsx                      # /dashboard — Service selector
    ├── nexus/
    │   ├── layout.tsx                # Client: NexusSidebar + processing indicator
    │   ├── page.tsx                  # /nexus — Documents + Analytics tabs
    │   ├── dashboard/page.tsx        # /nexus/dashboard → redirect → /nexus?tab=analytics
    │   ├── upload/page.tsx           # /nexus/upload
    │   ├── history/page.tsx          # /nexus/history
    │   └── insights/[jobId]/page.tsx # /nexus/insights/{jobId}
    └── zen/
        ├── layout.tsx                # Client: ZenHeader + ChatSidebar + ContextPanel
        ├── page.tsx                  # /zen — Landing with conversation starters
        ├── chat/[sessionId]/page.tsx # /zen/chat/{sessionId}
        ├── agents/page.tsx           # /zen/agents
        ├── documents/page.tsx        # /zen/documents
        ├── search/page.tsx           # /zen/search
        └── settings/page.tsx         # /zen/settings

Route Groups

Group Layout Auth Description
(auth) Server component Redirects if authenticated Login, Register
(dashboard) Server component + providers Required (session cookie) All protected pages

Deleted Routes

Route Reason
/nexus/stats Superseded by /nexus?tab=analytics
/nexus/dashboard Redirects to /nexus?tab=analytics

Authentication

Middleware (middleware.ts)

Runs on every request. Checks for sentinel_session cookie.

Route Pattern Behavior
/, /api/*, /_next/*, /favicon.ico, /public/* Pass through (public)
/login, /register If cookie present → redirect to /dashboard
/nexus/*, /zen/*, /dashboard/* If no cookie → redirect to /login

Defense-in-Depth

Middleware performs optimistic cookie-exists check only. Authoritative session verification happens in the (dashboard)/layout.tsx server component via DAL (Data Access Layer), following CVE-2025-29927 best practices.

Session Expiry Handling

verifySession (in lib/dal/auth.ts) deletes the sentinel_session cookie before redirecting to /login when the JWT is invalid or missing a userId. This prevents a redirect loop where the middleware sees the stale cookie, passes the request to the layout, which then redirects back to /login, repeating indefinitely. Session expiry is enforced by the JWT itself; there is no separate server-side expiresAt > Date.now() check.

Auth Store (stores/auth-store.ts)

interface AuthUser {
  id: string;
  email: string;
  username: string;
  firstName: string | null;
  lastName: string | null;
  avatar: string | null;
  role: 'viewer' | 'analyst' | 'admin';
  team: 'INTERNAL' | 'EXTERNAL';
}

interface AuthState {
  user: AuthUser | null;
  accessToken: string | null;
  isAuthenticated: boolean;
  isLoading: boolean;
  isHydrated: boolean;
  // Actions: setUser, setAccessToken, logout, clearClientState,
  //          broadcastLogout, broadcastLogin
}
  • Persistence: Zustand middleware, key sentinel-auth-v2
  • Cross-tab sync: BroadcastChannel API for login/logout propagation
  • Default role/team: viewer / EXTERNAL when not provided by backend

Theme System

Service Detection

URL path starts with /zen/* → Service: 'zen'
Otherwise                   → Service: 'nexus'

Theme Modes

Mode Nexus Service Zen Service
auto Light Dark
light Light Light
dark Dark Dark
terminal Terminal Terminal

CSS Class Application

Scheme HTML classes Body classes
Light light nexus light nexus
Dark dark zen dark zen
Terminal dark terminal dark terminal

Theme Provider

// Available hooks
useTheme()         // Full theme context (mode, scheme, service, setMode)
useService()       // 'nexus' | 'zen'
useIsZen()         // boolean
useIsNexus()       // boolean
useIsTerminal()    // boolean
useThemeClass()    // Theme-aware className helper
useThemeVariable() // Computed CSS variable value

Persistence: localStorage key sentinel-theme-mode.


State Management

Zustand Stores

Store Key State Persistence
auth-store User, tokens, auth state sentinel-auth-v2 (localStorage)
nexus-store Upload files, active jobs, filters, annotations, PDF linking None (session-only)
zen-store Sessions, messages, agents, context docs, UI state None (session-only)

React Query

All API data fetching uses TanStack React Query with:

  • Conditional polling: refetchInterval based on isTabVisible and processing state
  • Placeholder data: Previous data shown during refetch (no loading flicker)
  • Parallel queries: useQueries for batch job enrichment
  • Optimistic updates: Chat messages appear immediately
  • Stale time: 5 minutes for enrichment data, 30s for dashboard Zen session data

Dashboard Activity Feed

The main dashboard (/(dashboard)/page.tsx) fetches Zen chat sessions via React Query (queryKey: ['zenChatSessions'], staleTime: 30000) and merges them with Nexus pipeline entries into a unified activity feed. Sessions with the default title “New Chat” are excluded. Entries are sorted by updated_at descending.


API Architecture

Backend Services

Service Base URL API Client
Nexus NEXT_PUBLIC_NEXUS_API_URL lib/api/nexus.ts (88 functions)
Zen NEXT_PUBLIC_ZEN_API_URL lib/api/zen.ts (32 functions)
Studio Gateway NEXT_PUBLIC_STUDIO_API_URL lib/api/nexus-gateway.ts

Gateway Routing (Nexus)

Request → Studio Gateway available?
  ├── Yes → POST /api/v1/nexus/{path}
  │   ├── Success → Return response
  │   ├── 5xx/401 → Mark gateway unavailable, fallback
  │   └── 404 "No service route" → Fall through to direct
  └── No → Direct Nexus Backend POST /{path}

Gateway availability is tracked per-session. A single 5xx marks it unavailable for subsequent requests.

API Client Options (RequestOptions)

RequestOptions is exported from lib/studio/api-client.ts and accepted by all gateway layers.

Option Type Description
keepalive boolean Passes keepalive: true to fetch. Use for cleanup requests that must survive page navigation.
suppressAuthEvent boolean Suppresses the global auth:session-expired event on 401. Use for background polling loops to avoid disrupting the UI.
skipAuth boolean Skip Bearer token injection
rawResponse boolean Skip envelope unwrapping
signal AbortSignal Cancellation signal
apiVersion 'v1' \| 'v2' Override API version prefix (default: v1)

Environment Variables

Variable Required Purpose
NEXT_PUBLIC_NEXUS_API_URL Yes Nexus backend base URL
NEXT_PUBLIC_ZEN_API_URL Yes Zen chatbot backend base URL
NEXT_PUBLIC_STUDIO_API_URL No Studio gateway (enables routing)
NEXT_PUBLIC_ENABLE_MOCKS No Enable MSW mock service worker
NEXT_PUBLIC_APP_URL No Application URL for CORS
NEXT_PUBLIC_COOKIE_DOMAIN No Cookie domain for auth

Nexus Sidebar (components/nexus/sidebar.tsx)

Item Route Icon
Dashboard /nexus LayoutDashboard
Upload /nexus/upload Upload
History /nexus/history History
Analytics /nexus?tab=analytics BarChart3

Active route detection uses usePathname() + useSearchParams() for query-param-aware highlighting (Analytics tab requires ?tab=analytics match).

Zen Header (components/zen/zen-header.tsx)

Item Route Icon
Chat /zen MessageSquare
Agents /zen/agents Bot
Documents /zen/documents FileText
Search /zen/search Search
Profile /zen/settings User

Main Dashboard Quick Actions

Card Destination Service
Portfolio Intelligence /nexus Nexus
Document Pipeline /nexus/upload Nexus
Financial AI Chat /zen Zen
AI Agent Config /zen/agents Zen
Knowledge Search /zen/search Zen
Client Reporting /nexus/history Nexus

Build & Deploy

Build Output

output: 'standalone'     # Self-contained Node.js server
compress: true            # Gzip compression
images.unoptimized: true  # CloudFront handles image optimization

Deployment Pipeline

TypeScript Check → Next.js Build → Docker Build (linux/amd64)
→ ECR Push → ECS Force Deploy → CloudFront Invalidation

Infrastructure

Component Resource
Container ECS Fargate (1 task, 256 CPU / 512 MB)
Registry ECR sentinel-frontend-dev
Load Balancer ALB sentinel-frontend-dev-alb
CDN CloudFront EQEH6ZAHOVUNL
DNS Route53 → sentinel-dev.centricitywealth.tech
Region ap-south-1 (Mumbai)

Docker Build Args

ARG NEXT_PUBLIC_NEXUS_API_URL
ARG NEXT_PUBLIC_ZEN_API_URL
ARG NEXT_PUBLIC_AGENTS_API_URL
ARG NEXT_PUBLIC_KNOWLEDGE_API_URL
ARG NEXT_PUBLIC_STUDIO_API_URL
ARG NEXT_PUBLIC_ENABLE_MOCKS=false

Security Headers

All routes receive: X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy: camera=(), microphone=(), geolocation=().

Static assets (/_next/static/): Cache-Control: public, max-age=31536000, immutable.


Testing

Playwright Configuration

Project Device Auth
chromium Desktop Chrome Authenticated
firefox Desktop Firefox Authenticated
webkit Desktop Safari Authenticated
mobile-chrome Pixel 5 Authenticated
mobile-safari iPhone 13 Authenticated

Test Fixtures

  • static/assets/test-user.json — Test credentials
  • static/assets/test-zen.json — Zen chat test queries
  • static/assets/*.pdf — 14 financial document PDFs (PMS/AIF/MF)

Test Suites

Suite Coverage
all-routes.spec.ts Every route loads without errors
page-rendering.spec.ts Key UI elements render per page
user-journeys.spec.ts End-to-end flows (login → upload → insights)
auth-flows.spec.ts Login, register, session expiry, cross-tab
error-boundaries.spec.ts Error states, network failures, retry
loading-states.spec.ts Skeleton loaders, spinners, transitions
screenshot-all-pages.spec.ts Visual regression (21 reference screenshots)

Last updated: 2026-03-03 Version: 6.2.0