Reference architecture
Every box. Every reason.
Tap any component to see what it does, why it's there, and what it costs. The diagram is the architecture — nothing is hidden in footnotes.
Distribution
~$2/moCloudFront
Public HTTPS front door for app + API.
One distribution serves both the Next.js app (default behaviour) and the API (/revenue/*, /chat/*, /ai/*, etc.). Injects an origin-verify header so Amplify rejects direct hits.
Request flow
Watch a single question become an answer.
Nine checkpoints, from the keystroke in the browser to the streamed response. Every step either verifies identity, scopes data, or does both.
- Keystroke
- CloudFront
- WAF
- API Gateway
- Lambda
- Postgres
- Bedrock
- DynamoDB
- Stream back
Step 1 · Client
KeystrokeUser types a question in the chat textarea. The Next.js client validates it locally and attaches the Cognito id token from in-memory state.
Security
Four independent layers, each blocking a different class of attack.
01
AWS WAF — perimeter
OWASP Top 10 managed rule set, known-bad-inputs (Log4j, SSRF), IP reputation list, and a rate-based rule that blocks any IP sending more than 100 requests in a 5-minute window.
02
Cognito JWT — API boundary
API Gateway rejects every request without a valid Cognito JWT before Lambda is invoked. No authenticated caller means no AI compute runs, which eliminates the cost exposure open endpoints typically carry.
03
Row-level security — data layer
Postgres enforces the tenant_id claim from the JWT on every SELECT, INSERT, UPDATE, and DELETE. A bug in the application layer cannot leak cross-tenant data.
04
Origin verify — bypass prevention
Amplify basic-auth rejects direct origin hits. CloudFront injects a shared secret on every request. Anyone who discovers the raw Amplify URL receives a 401 — the WAF cannot be bypassed.
Multi-tenancy
Enforced in Postgres, not in the application.
Every API request carries a Cognito JWT. Lambda extracts the custom:tenant_id claim and sets it as a Postgres session variable before any query runs.
Row-level security policies on every table enforce that variable. Tenant A can never read tenant B's rows — not because the application filtered them out, but because the database rejected the read outright.
AI layer
Natural language in. Tenant-scoped data out.
The Ask endpoint pre-fetches three context slices from the tenant's database — revenue by day, top menu items, recent orders — and injects them as grounded context before calling Amazon Nova Lite on Bedrock via a cross-region inference profile.
The JWT authorizer rejects unauthenticated requests at API Gateway before any Lambda is invoked. The AI compute never runs for an unverified caller — so there is no cost exposure from open endpoints.
By the numbers
- CDK stacks
- 9
- Security layers
- 4
- Lambda functions
- 5
- WAF rule groups
- 4
one cdk deploy
independent
Python 3.12
OWASP + rate limit