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

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: 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

Stack Outputs

The CloudFormation stack exports these outputs for deployment scripts:

Infrastructure Deployment

See Deploying the API and Deploying the Web App for application deployment. For infrastructure deployment:
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.