High-Level Architecture
Component Details
Frontend - Next.js 15 Static Export
The frontend uses
output: 'export' which requires all routes to be known at build time. Dynamic [id] segments are not supported - all entity IDs are passed via query parameters (e.g., /assessments/upload?id=uuid).Backend - NestJS 10 REST API
main.ts- Local development server (port 3001)lambda.ts- AWS Lambda handler for API Gatewayworker.ts- Background job processor
API Lambda Environment Variables
API Lambda Environment Variables
Database - PostgreSQL 15 via Prisma
Schema Overview (seepackages/api/prisma/schema.prisma):
- Core Models
- Risk Data
- Job System
- Prompts (Admin)
Authentication - AWS Cognito
User Pool Configuration:- Email-based sign-in (username = email)
- Password policy: 8+ chars, uppercase, lowercase, number, special char
- MFA: Optional (disabled by default)
- Admin group:
admin(checked byAdminGuardin NestJS)
- Access Token: 60 minutes (short-lived for API requests)
- Refresh Token: 30 days (if Remember Me checked) or session-only
- Auto-Refresh: API client intercepts 401 responses, uses refresh token, retries request
Token Storage Implementation
Token Storage Implementation
AI Pipeline - AWS Bedrock Multi-Agent System
Model Configuration (seepackages/shared/src/constants/bedrock.config.ts):
1
Parser Agent
Input: S3 URI to uploaded PDF/DOCXProcess:
- Fetch document from S3
- Extract text using AWS Textract (for PDFs) or raw text (DOCX)
- Send text to Bedrock with parser prompt
- Extract structured data across 35 risk indicators
2
Gap Detector Agent
Input: Parsed data from Step 1 + expected field list (35 indicators)Process:
- Compare extracted data against required fields
- Validate data quality (completeness, format, confidence)
- Mark each field as VERIFIED, PARTIAL, or MISSING
GapField records inserted into database3
Risk Analysis Agent
Input: All gap fields (extracted + corrected values)Process:
- For each of 7 categories:
- Score 5 subcategories (0-100 scale)
- Map to risk level: 0-33 Low, 34-66 Medium, 67-100 High
- Generate evidence text citing data sources
- Calculate weighted average for category score
- Generate narrative explaining risk drivers
- Create 3-5 prioritized recommendations per category
RiskScore records + associated Recommendation records4
Report Generator Agent
Input: Assessment ID (fetches all data from database)Process:
- Query all gap fields, risk scores, recommendations, comments
- Generate HTML report template with:
- Executive summary
- Company profile
- Risk scorecard (7 categories + 35 subcategories)
- Evidence and narratives
- Prioritized recommendations
- Appendices (methodology, data sources)
- Convert HTML to PDF using headless Chrome (Puppeteer in Lambda)
- Upload PDF to S3
- Generate pre-signed download URL (1-hour expiry)
Agent Chaining: The job system automatically chains
PARSE_DOCUMENT → GAP_DETECTION. Analysts manually trigger RISK_ANALYSIS and REPORT_GENERATION after reviewing gaps.Asynchronous Job System
Fire-and-Forget Pattern: Job Handler Interface (packages/api/src/jobs/job-handler.interface.ts):
- Max attempts: 3 (configurable per job)
- Backoff: Exponential (1s, 2s, 4s)
- Permanent failure: Status set to
FAILED, error message stored - Document status updated:
PARSING→FAILED
Infrastructure - AWS CloudFormation
Stack Resources (infra/lib/alliance-risk-stack.ts):
- Compute
- Storage
- Networking
- Auth & CDN
Data Flow Examples
Example 1: Document Upload and Parsing
Example 2: Risk Analysis and Scoring
Performance Characteristics
Document Parsing
Typical: 30-60 seconds for 10-30 page PDFsVariables:
- Document length (pages)
- Text density
- Textract processing time
- Bedrock throttling
Gap Detection
Typical: 10-20 secondsVariables:
- Number of extracted fields
- Validation complexity
Risk Analysis
Typical: 60-90 seconds (7 categories × 10-15s each)Variables:
- Bedrock API latency
- Prompt complexity
- Number of recommendations
Report Generation
Typical: 20-30 secondsVariables:
- Report length
- PDF rendering complexity
- S3 upload speed
- API Lambda: 1-2 seconds (arm64, 1024MB, bundled with esbuild)
- Worker Lambda: 2-3 seconds (arm64, 1024MB, Prisma client generation)
- Mitigation: Provisioned concurrency for production (not enabled in MVP)
Security Model
1
Authentication
- AWS Cognito User Pool with email + password
- JWT tokens (access: 60min, refresh: 30 days)
- Token rotation on refresh
- Rate limiting: 5 req/min on auth endpoints (NestJS Throttler)
2
Authorization
- Global
JwtAuthGuardon all API routes (except@Public()) - JWT signature verification against Cognito JWKS
- User roles via Cognito groups (
admingroup) AdminGuardcheckscognito:groupsclaim- Resource ownership validation (userId must match createdById)
3
Data Encryption
- In Transit: HTTPS only (CloudFront, API Gateway, S3 pre-signed URLs)
- At Rest: RDS encrypted with AWS KMS, S3 SSE-AES256
- Secrets: Database credentials in Secrets Manager, auto-rotation enabled
4
Network Isolation
- RDS in private VPC subnets (no internet gateway)
- Lambda functions in VPC to access RDS
- VPC endpoint for Cognito (interface endpoint)
- S3 and Bedrock accessed via NAT Gateway (or VPC endpoints in production)
5
Input Validation
- NestJS
ValidationPipewithclass-validatoron all DTOs - File upload limits: 10MB max, PDF/DOCX only (MIME type validation)
- SQL injection prevention: Prisma ORM (parameterized queries)
- XSS prevention: React auto-escaping, CSP headers on CloudFront
Monitoring and Observability
CloudWatch Logs:/aws/lambda/alliance-risk-api- API request/response, errors/aws/lambda/alliance-risk-worker- Job processing, Bedrock calls, failures/aws/rds/cluster/alliance-risk/postgresql- Slow queries, errors
- Lambda invocations, duration, errors, throttles
- RDS connections, CPU, memory, storage
- Bedrock API latency, throttling, errors
- S3 request counts, error rates
- RDS db.t3.micro: $15
- Lambda (API + Worker): $5-10 (low traffic)
- Bedrock (Claude 3.5 Sonnet): $20-50 (100-200 assessments/month)
- S3: $2
- CloudFront: $5
- Total: ~$50-80/month
Development Workflow
- Local Setup
- Testing
- Deployment
- Debugging
Scalability Considerations
Current Limits (MVP configuration):- RDS: 100 concurrent connections (db.t3.micro)
- Lambda: 10 concurrent executions (soft limit, can increase)
- Bedrock: 5 requests/second (default throttle per model)
- S3: 5,500 GET/3,500 PUT per second per prefix (effectively unlimited)
Horizontal Scaling
Horizontal Scaling
- RDS: Upgrade to db.r6g.xlarge for 100+ concurrent users
- Lambda: Increase reserved concurrency for API and Worker
- Bedrock: Request quota increase (up to 1000 req/s per model)
- CloudFront: No action needed, auto-scales globally
Optimization
Optimization
- Database: Add indexes on
assessmentId,userId,statuscolumns - Caching: Add Redis/ElastiCache for session storage, prompt caching
- CDN: Cache API responses for read-heavy endpoints (
GET /api/prompts/section/:section) - Bedrock: Batch subcategory scoring (1 call instead of 5 per category)
Cost Optimization
Cost Optimization
- Lambda: Use ARM64 (20% cheaper, already implemented)
- RDS: Enable auto-pause for dev/staging (Aurora Serverless v2)
- S3: Lifecycle policy to Glacier after 90 days
- Bedrock: Switch to Claude 3 Haiku for parsing (5x cheaper, acceptable accuracy)