Skip to main content
The CGIAR Risk Intelligence Tool runs entirely on AWS infrastructure, using a serverless architecture with managed services for scalability, security, and cost optimization.

Architecture Diagram

CloudFront (CDN)
    |
    ├─> S3 (Static Web Hosting)          Next.js SPA
    |
API Gateway (HTTP API)
    |
    └─> API Lambda (NestJS)
            ├─> Cognito User Pool          Authentication
            ├─> RDS PostgreSQL (VPC)       Primary database
            ├─> S3 (File Bucket)           Business plan uploads
            ├─> Bedrock (Claude 3.5)       AI agents (9 agents, 7 KBs)
            ├─> Textract                   Document parsing
            └─> Worker Lambda              Async job processing

Core AWS Resources

Cognito User Pool

Purpose: User authentication and authorization
  • Authentication: Email-based login with AWS Cognito User Pool
  • Password Policy:
    • Minimum 12 characters
    • Requires uppercase, lowercase, numbers, and symbols
    • Temporary password valid for 7 days
  • User Groups: admin group for platform administrators
  • Token Validity:
    • Access token: 60 minutes
    • ID token: 60 minutes
    • Refresh token: 30 days (43,200 minutes)
  • Auth Flows: USER_PASSWORD_AUTH, ADMIN_USER_PASSWORD_AUTH, USER_SRP_AUTH, REFRESH_TOKEN_AUTH
  • Self-signup: Disabled (admin-only user creation)
Stack Resource: UserPool, UserPoolClient, AdminGroup

RDS PostgreSQL 15

Purpose: Primary relational database for all application data
  • Instance Type: db.t3.micro (2 vCPUs, 1 GiB RAM)
  • Storage: 20 GB General Purpose SSD (gp3)
  • Engine: PostgreSQL 15
  • Database Name: alliance_risk
  • High Availability: Single-AZ (MVP configuration)
  • Backup Retention: 7 days
  • Encryption: Storage encrypted at rest
  • Network: Private VPC, no public access
  • Deletion Protection: Enabled (production)
  • Deletion Policy: Snapshot (production)
Credentials: Stored in AWS Secrets Manager at alliance-risk/db-credentials Schema Management: Prisma ORM with migrations in packages/api/prisma/migrations/ Stack Resource: Database, DbSecret, DbSecurityGroup, DbSubnetGroup

Lambda Functions

Both Lambda functions run on ARM64 architecture for cost optimization (20% cheaper than x86_64).

API Lambda

Purpose: REST API server (NestJS)
  • Function Name: alliance-risk-api
  • Runtime: Node.js 20.x (ARM64)
  • Handler: dist/src/lambda.handler
  • Memory: 1024 MB
  • Timeout: 29 seconds (< API Gateway 30s limit)
  • VPC: Attached to private subnets for RDS access
  • Trigger: API Gateway HTTP API
  • Environment Variables:
    • ENVIRONMENT — Deployment environment (dev/staging/production)
    • COGNITO_USER_POOL_ID — Cognito User Pool ID
    • COGNITO_CLIENT_ID — Cognito App Client ID
    • S3_BUCKET_NAME — File storage bucket name
    • AWS_ACCOUNT_ID — AWS Account ID
    • WORKER_FUNCTION_NAME — Worker Lambda function name
    • CORS_ORIGIN — Allowed CORS origin
    • DATABASE_URL — PostgreSQL connection string (constructed from Secrets Manager)
Stack Resource: ApiLambda, ApiLambdaRole

Worker Lambda

Purpose: Background job processor for long-running AI operations
  • Function Name: alliance-risk-worker
  • Runtime: Node.js 20.x (ARM64)
  • Handler: dist/src/worker.handler
  • Memory: 1024 MB
  • Timeout: 900 seconds (15 minutes)
  • VPC: Attached to private subnets for RDS access
  • Invocation: Asynchronous (fire-and-forget from API Lambda)
  • Environment Variables:
    • ENVIRONMENT — Deployment environment
    • AWS_ACCOUNT_ID — AWS Account ID
    • S3_BUCKET_NAME — File storage bucket name
    • DATABASE_URL — PostgreSQL connection string
Stack Resource: WorkerLambda, WorkerLambdaRole Special Action: run-sql — Executes raw SQL on RDS for database migrations (RDS is in private VPC, unreachable from local machines)

S3 Buckets

File Storage Bucket

Purpose: Business plan document uploads (PDF/DOCX)
  • Bucket Name: alliance-risk-files-{AWS::AccountId}
  • Public Access: Blocked (all)
  • Encryption: S3-managed (AES256)
  • Versioning: Disabled
  • Deletion Policy: Retain (production)
  • CORS: Enabled for presigned upload URLs
    • Allowed origins: *
    • Allowed methods: GET, PUT
    • Exposed headers: ETag
Stack Resource: FileBucket

Web Hosting Bucket

Purpose: Next.js static site hosting
  • Bucket Name: alliance-risk-web-{AWS::AccountId}
  • Public Access: Blocked (CloudFront OAI used)
  • Encryption: S3-managed (AES256)
  • Versioning: Disabled
  • Deletion Policy: Delete (auto-cleanup on stack deletion)
Stack Resource: WebBucket, WebBucketPolicy

CloudFront Distribution

Purpose: Global CDN for static web application
  • Origin: S3 Web Bucket via Origin Access Identity (OAI)
  • Protocol: HTTPS only (redirect HTTP → HTTPS)
  • Price Class: 100 (North America + Europe)
  • HTTP Version: HTTP/2
  • Default Root Object: index.html
  • Error Responses: 403/404 → 200 with /index.html (SPA routing)
  • Cache Policy: CachingOptimized (658327ea-f89d-4fab-a63d-7e88639e58f6)
  • Compression: Gzip enabled
URL Rewrite Function: CloudFront Function that rewrites extensionless URLs to .html files for Next.js static export:
  • /login/login.html
  • /dashboard//dashboard.html
  • Blocks direct browser navigation to .txt files (RSC payloads)
Stack Resource: WebDistribution, WebOAI, UrlRewriteFunction

API Gateway HTTP API

Purpose: HTTP API endpoint for REST API
  • Name: alliance-risk-api
  • Protocol: HTTP (not REST — simpler, cheaper)
  • Integration: Lambda Proxy (payload format 2.0)
  • CORS:
    • Allowed origins: *
    • Allowed methods: GET, POST, PUT, DELETE, OPTIONS, PATCH
    • Allowed headers: *
    • Max age: 86400 seconds (24 hours)
  • Authorization: None (handled by NestJS JWT guards)
  • Default Route: All requests route to API Lambda
Stack Resource: HttpApi, HttpApiIntegration, HttpApiDefaultRoute, HttpApiStage

IAM Roles and Policies

API Lambda Role

Permissions:
  • CloudWatch Logs: Basic Lambda execution (log creation)
  • VPC Access: ENI creation for VPC attachment
  • Cognito: All admin user/group operations
  • Bedrock: Model invocation and knowledge base retrieval
    • anthropic.claude-3-5-sonnet-*
    • anthropic.claude-3-haiku-*
  • Textract: Document analysis
  • S3: GetObject, PutObject, DeleteObject on FileBucket
  • Lambda: InvokeFunction on Worker Lambda
Stack Resource: ApiLambdaRole

Worker Lambda Role

Permissions (identical to API Lambda except no Worker invocation):
  • CloudWatch Logs: Basic Lambda execution
  • VPC Access: ENI creation for VPC attachment
  • Cognito: All admin user/group operations
  • Bedrock: Model invocation and knowledge base retrieval
  • Textract: Document analysis
  • S3: GetObject, PutObject, DeleteObject on FileBucket
Stack Resource: WorkerLambdaRole

VPC Networking

The RDS database resides in a private VPC with no public internet access. Both Lambda functions are deployed inside the same VPC to communicate with the database.

VPC Configuration

  • VPC: Default VPC (specified via VpcId parameter)
  • Subnets: Public subnets (specified via SubnetIds parameter)
  • Security Groups:
    • LambdaSecurityGroup — Allows all outbound traffic
    • DbSecurityGroup — Allows inbound PostgreSQL (5432) from LambdaSecurityGroup only
    • VpcEndpointSecurityGroup — Allows HTTPS (443) from LambdaSecurityGroup

VPC Endpoints

Lambdas in a VPC cannot reach public AWS services (Cognito, Bedrock, S3) without VPC Endpoints or a NAT Gateway. The stack provisions the following VPC endpoints:
ServiceTypePurpose
Cognito IDPInterfaceToken verification, user management
LambdaInterfaceWorker Lambda invocation from API Lambda
TextractInterfaceDocument parsing
S3GatewayFile storage access (cheaper than interface endpoint)
Important: Interface endpoints only support specific Availability Zones. The EndpointSubnetIds parameter must specify subnets in supported AZs (e.g., us-east-1a/b/c for Cognito). Missing Endpoint: Bedrock does not have a VPC endpoint yet. The current implementation uses a NAT Gateway (not in this template) or direct internet access. This will be addressed in a future update.

Security Group Rules

LambdaSecurityGroup:
  Egress: 0.0.0.0/0 (all traffic)

DbSecurityGroup:
  Ingress: LambdaSecurityGroup → 5432/tcp

VpcEndpointSecurityGroup:
  Ingress: LambdaSecurityGroup → 443/tcp

Stack Outputs

The CloudFormation stack exports these outputs for deployment scripts:
Output KeyDescriptionUsed By
ApiUrlAPI Gateway endpoint URLWeb build (NEXT_PUBLIC_API_URL)
CloudFrontUrlCloudFront distribution URLWeb deployment verification
CloudFrontDistributionIdCloudFront distribution IDWeb deployment (cache invalidation)
CognitoUserPoolIdCognito User Pool IDWeb build (NEXT_PUBLIC_COGNITO_USER_POOL_ID)
CognitoClientIdCognito App Client IDWeb build (NEXT_PUBLIC_COGNITO_CLIENT_ID)
FileBucketNameS3 file storage bucket nameAPI deployment
WebBucketNameS3 web hosting bucket nameWeb deployment
DatabaseEndpointRDS endpoint addressDatabase migrations
DeployBucketNameS3 deployment bucket nameAPI deployment
WorkerLambdaNameWorker Lambda function nameAPI deployment

Infrastructure Deployment

See Deploying the API and Deploying the Web App for application deployment. For infrastructure deployment:
cd infra
pnpm cfn:deploy dev   # dev | staging | production
See infra/CLAUDE.md for CDK vs CloudFormation deployment options.

Cost Optimization

  • ARM64 Architecture: 20% cost reduction on Lambda
  • HTTP API: Cheaper than REST API Gateway (~70% savings)
  • Gateway VPC Endpoint for S3: No data transfer charges (vs interface endpoint)
  • CloudFront Price Class 100: Cheaper edge locations (North America + Europe)
  • Single-AZ RDS: No multi-AZ replication charges (acceptable for MVP)
  • gp3 Storage: Better price/performance than gp2

Security Best Practices

  • Secrets Manager: Database credentials never hardcoded
  • VPC Isolation: RDS has no public internet access
  • S3 Block Public Access: All buckets private by default
  • Encryption at Rest: RDS and S3 encrypted
  • Encryption in Transit: HTTPS only for all services
  • Least Privilege IAM: Lambda roles grant only required permissions
  • CloudFront OAI: S3 bucket accessible only via CloudFront
  • Security Groups: Database accessible only from Lambda functions

Disaster Recovery

  • RDS Automated Backups: 7-day retention
  • RDS Deletion Policy: Snapshot on stack deletion (production)
  • S3 Versioning: Disabled (not required for MVP)
  • CloudFormation Drift Detection: Run aws cloudformation detect-stack-drift periodically

Monitoring and Logging

  • CloudWatch Logs:
    • /aws/lambda/alliance-risk-api — API Lambda logs
    • /aws/lambda/alliance-risk-worker — Worker Lambda logs
  • CloudWatch Metrics:
    • Lambda invocations, errors, duration, throttles
    • API Gateway 4xx/5xx errors, latency
    • RDS CPU, storage, connections
  • X-Ray Tracing: Not enabled (optional enhancement)
See CloudWatch Logs for troubleshooting Lambda errors.

Build docs developers (and LLMs) love