Production RAG chatbot on AWS Bedrock, serverless AI architectures, and cloud ops projects. Built on Lambda, API Gateway, SambaNova.
Live App
🚦
NTCIP Traffic Controller Simulator (TCS)
Simulate a NEMA dual-ring 8-phase traffic controller. Adjust timing plans, watch phases cycle live, and explore real NTCIP 1202 MIB objects.
Live App
Details →
🚦
Live Traffic Dashboard
Live traffic incident map across U.S. cities with Leaflet.js, color-coded markers, real-time filtering, and auto-refresh.
Live App
Details →
📋
Log Analyzer
Upload a server log and get an AI-powered summary of errors, anomalies, and key issues.
Live App
Details →
🎯
AI Resume ↔ Job Matcher
Upload your resume and a job description. Claude scores the match, flags missing skills, and highlights your strengths.
Live App
Details →
🔊
Text to Audio
Upload a .txt file and convert it to spoken audio. Adjust voice, speed, and pitch.
Live App
Details →
About: NTCIP Traffic Controller Simulator (TCS)
An interactive browser-based simulator of a real NEMA dual-ring, 8-phase traffic signal controller — the same controller model used at intersections across the United States. Adjust cycle length, phase splits, and yellow clearance time, and watch a live 4-way intersection respond in real time. Built to learn and demonstrate the NTCIP 1202 standard.
Stack: S3 Static Website · Single self-contained HTML/CSS/JS · HTML5 Canvas · No backend Key fact: Entire simulation runs in the browser using requestAnimationFrame. No server compute, no API calls, no data storage. The controller state machine, ring/barrier logic, and NTCIP OID values are all calculated client-side.
⚙️ Pillar 1 — Operational Excellence
Area
Current
Production Design
Reasoning
Deployment
Manual S3 upload via CLI
GitHub Actions CI/CD — push to main triggers s3 sync
Single-file app; CI/CD is a one-time setup that eliminates all future manual steps
Versioning
None
S3 Versioning enabled; rollback with one command
A broken deploy of an educational tool is easy to miss; versioning enables instant recovery
Observability
None
S3 Server Access Logs; optional JS error tracking
Knowing which presets users interact with most guides future feature development
Health Check
None
UptimeRobot 5-min ping on S3/CloudFront endpoint
Static sites can silently go down; external ping is the simplest detection method
🔒 Pillar 2 — Security
Area
Current
Production Design
Reasoning
Transport
HTTP (S3 website endpoint)
HTTPS via CloudFront + ACM certificate
No HTTPS means browsers may warn users; CloudFront provides HTTPS at no extra compute cost
Bucket Access
Public-read ACL
Private bucket + CloudFront OAC
Public-read buckets are a common audit finding; OAC gives the same result with less exposure
No User Data
No data collected — pure simulation
No change — document this explicitly; no privacy policy required
Zero data collection is the strongest possible privacy posture
Content Security Policy
None
CloudFront adds CSP header restricting script sources to self
Prevents script injection attacks; especially important for educational tools that may be shared broadly
🛡️ Pillar 3 — Reliability
Area
Current
Production Design
Reasoning
Availability
S3 99.9% SLA
CloudFront edge cache — serves from cache even during S3 disruptions
A single static file cached at edge is effectively always available
An educational portfolio tool will never approach CloudFront free tier limits
Cost Tagging
No tags on S3 bucket
Tag: Project=ntcip-simulator, Owner=jimmy-hubbard
Consistent tagging enables Cost Explorer breakdown as the project count grows
Estimated Monthly
~$0.00
~$0.00
Pure static hosting with client-side compute — no variable costs
🌱 Pillar 6 — Sustainability
Area
Current
Production Design
Reasoning
Compute Footprint
Zero server compute — simulation runs on user’s device
No change — this is the most sustainable compute model possible
No dedicated server runs on behalf of the operator; all energy consumed is on the user’s existing device
Data Transfer
45KB served from us-east-1
CloudFront edge delivery — shorter path reduces transit energy
Serving from an edge node 50ms away uses less backbone network energy than routing from us-east-1
Frame Rate
60fps requestAnimationFrame loop
Throttle to 30fps — imperceptible for signal timing simulation
Halving frame rate halves GPU energy consumption on the user’s device with no visible quality loss
No Idle Compute
Simulation pauses when browser tab is hidden (browser throttles rAF)
Add Page Visibility API listener to fully stop the loop when tab is hidden
Browsers throttle rAF but don’t stop it; explicit pause eliminates CPU usage on hidden tabs
📊 Current vs. Production Summary
Dimension
Current
Production
Effort
Transport Security
HTTP
HTTPS via CloudFront
Medium
Bucket Access
Public-read
Private + OAC
Medium
Deployment
Manual upload
GitHub Actions CI/CD
Medium
Canvas Layering
Full redraw each frame
Static + dynamic layers
Low
State Persistence
Resets on reload
localStorage save/restore
Low
Frame Rate
60fps always
30fps throttle + tab visibility
Low
Caching
No Cache-Control
max-age=3600 + hash
Low
Monthly Cost
~$0.00
~$0.00
No change
Production Architecture — Well-Architected Design
What I Learned
This project taught me more domain knowledge than technical skills. Understanding NEMA dual-ring logic, barrier synchronization, and how timing plans are engineered required working directly with standards documentation—not just writing code. Building it from scratch was the fastest way to truly understand how the system operates.
About: Text to Audio
This web app allows anyone to instantly convert written text into spoken audio — no downloads, no accounts, no cost. Simply upload a .txt file or paste any text, choose a voice, adjust the speed and pitch, and press Play. Built and deployed entirely through the cloud, it runs directly in the browser using native speech technology.
Project: Text to Audio · Reviewed: 2026-03-19 · Architect: Jimmy Hubbard
Stack: S3 Static Website · HTML/CSS/JS · Web Speech API (SpeechSynthesis) Key fact: No backend. No Lambda. No Polly. All audio synthesis runs natively in the user’s browser — zero AWS compute per request.
⚙️ Pillar 1 — Operational Excellence
Area
Current
Production Design
Reasoning
Deployment
Manual S3 upload via CLI
GitHub Actions CI/CD — aws s3 sync on every push to main
Eliminates human error; every deploy tied to a commit
Rollback
Re-upload previous file manually
S3 Versioning enabled; restore prior version with one command
30-day version retention allows fast, safe rollback
Enables Cost Explorer filtering as the project portfolio grows
Estimated Monthly
~$0.00
~$0.00 (no change)
This is the most cost-efficient architecture possible — no cloud compute per request
🌱 Pillar 6 — Sustainability
Area
Current
Production Design
Reasoning
Compute Footprint
Zero AWS compute — no EC2, Lambda, or containers
No change — this is the most sustainable compute model possible
All processing runs on the user’s existing device; no dedicated server runs on Jimmy’s behalf
Data Transfer
Served from us-east-1 to all users globally
CloudFront edge delivery — shorter network path = less transit energy
Reducing long-haul data transfer reduces energy consumed in transit
Audio Engine
Native OS speech synthesis — no audio streamed from cloud
No change
Streaming from a cloud TTS (Polly) would consume Lambda, S3, CloudFront, and audio data per request. Current architecture avoids all of that.
Right-Sizing
No servers to size
No change — zero idle capacity exists
No provisioned infrastructure means no right-sizing work required
📊 Current vs. Production Summary
Dimension
Current
Production
Effort
Transport Security
HTTP only
HTTPS via CloudFront + ACM
Medium
Bucket Access
Public-read ACL
Private + CloudFront OAC
Medium
Deployment
Manual S3 upload
GitHub Actions CI/CD
Medium
Versioning
None
S3 Versioning enabled
Low
Observability
None
S3 Access Logs + uptime check
Low
Caching
No Cache-Control
max-age=3600 via CloudFront
Low
Browser Compat
Silent failure
JS feature-detection warning
Low
Voice Loading Guard
None
onvoiceschanged event handler
Low
Text Chunking
Single utterance
Sentence-boundary JS chunking
Medium
Cost Tagging
No tags
Project/Owner/Environment tags
Low
Monthly Cost
~$0.00
~$0.00
No change
Compute Used
Zero (client-side)
Zero (client-side, no change)
No change
Production Architecture — Well-Architected Design
What I Learned
Working with browser-based audio APIs and file handling showed me how much complexity exists in seemingly simple features. Managing file encodings, handling audio playback states, and providing meaningful user feedback during processing were all more involved than expected.
About: Log Analyzer
Upload a server log file and let AI do the heavy lifting. Paste or upload a log, hit Analyze, and within seconds you get a structured report — errors flagged, anomalies spotted, key events summarized. No files stored, no waiting: the log text goes directly to a Lambda function via a secure Function URL, which sends it to the Anthropic API (Claude Haiku) for analysis. Results come back in the same request.
Project: Log Analyzer · Reviewed: 2026-03-19 · Architect: Jimmy Hubbard
Stack: S3 Static Website · HTML/CSS/JS · Lambda Function URL · Anthropic API (Claude Haiku) Key fact: No API Gateway. Log text is sent directly in the POST body to a Lambda Function URL. Claude Haiku performs the analysis synchronously via the Anthropic API and the result is returned in the HTTP response — no S3 storage of results.
⚙️ Pillar 1 — Operational Excellence
Area
Current
Production Design
Reasoning
Deployment
Manual S3 upload + manual Lambda zip deploy via CLI
GitHub Actions: aws s3 sync for frontend, aws lambda update-function-code for backend on every push to main
Ties every production change to a commit; eliminates manual steps that can drift
Rollback
Re-upload previous zip manually
S3 Versioning on frontend; Lambda publishes versioned aliases — rollback with one alias pointer update
Versioned aliases allow instant rollback without redeployment
Observability
Lambda logs to CloudWatch Logs automatically; no custom metrics
Structured JSON logging from Lambda; custom CloudWatch metric for analysis latency; dashboard for request volume and error rate
Structured logs enable CloudWatch Insights queries; latency metric catches API degradation early
Health Check
No external check
UptimeRobot pings the S3 website endpoint every 5 min; synthetic canary hits Lambda Function URL with a test payload
Both frontend and backend need independent health verification
Malformed input can cause confusing API errors or silent truncation
IAM Role
No AWS IAM permissions needed for AI inference; Anthropic API key stored as encrypted Lambda environment variable
API key rotation via Anthropic console; Lambda env var updated with aws lambda update-function-configuration
Storing the API key in an environment variable keeps it server-side and never exposed to the browser
Bucket Access
Public-read for static hosting
Private bucket + CloudFront OAC
Public-read buckets are a common misconfiguration target
🛡️ Pillar 3 — Reliability
Area
Current
Production Design
Reasoning
API Timeouts
Lambda timeout 30s; Anthropic API typically responds in 3–8s for standard logs
Set max_tokens cap; chunk logs > 20K tokens before sending; return partial analysis if chunked
Claude Haiku has context limits; oversized payloads cause errors or silent truncation
Lambda Cold Start
Cold starts possible after idle periods — adds 200–500ms
Lambda SnapStart (if Python support available) or provisioned concurrency for one warm instance
Log analysis is latency-sensitive from a UX perspective; warm instances eliminate cold-start variance
Error Handling
Unhandled API exceptions bubble to frontend as 500
Try/except around Anthropic API call; return structured error JSON with retry guidance; CloudWatch alarm on Lambda errors
Users need actionable error messages; unhandled exceptions waste debugging time
Availability
S3 99.9% SLA; Lambda 99.95%; Anthropic API 99.9%+
CloudFront edges serve cached frontend during S3 disruptions; Anthropic API has strong uptime SLA; no AWS service dependency for AI inference
Anthropic API availability is independent of AWS regional issues
⚡ Pillar 4 — Performance Efficiency
Area
Current
Production Design
Reasoning
Lambda Memory
Default 128MB
Benchmark at 256MB and 512MB — Lambda CPU scales with memory; JSON parsing is CPU-bound
Doubling memory often cuts execution time by 40%+ for CPU-heavy work, at minimal cost increase
AI Model
Claude Haiku — fast, cost-effective
No change for standard logs; optionally route logs > 10K chars to Claude Sonnet for deeper analysis
Claude Haiku handles most logs well; Sonnet only justified for very large or complex log files
Frontend Delivery
S3 us-east-1 — latency grows with user distance
CloudFront edge caching — sub-50ms load times globally for the static HTML
Frontend is trivially cacheable; edge delivery is free within CloudFront free tier
Payload Size
Full log text sent in POST body; no compression
Frontend gzip-encodes POST body before send; Lambda reads Content-Encoding: gzip
Log files compress 80-90%; reduces transfer time and Lambda ingress processing
💰 Pillar 5 — Cost Optimization
Area
Current
Production Design
Reasoning
Anthropic API Cost
Claude Haiku: $0.80/MTok input, $4.00/MTok output
Spending limit set in Anthropic console; token cap in Lambda prevents runaway costs on huge logs
A 10,000-char log ≈ 2,500 tokens ≈ $0.002 per analysis. 1,000 analyses/month ≈ $2.00
Lambda Cost
First 1M requests/month free; 400K GB-seconds free
Stays within free tier at personal-project scale
Even at 10,000 analyses/month with 5s average duration at 128MB = well within free tier
S3 Cost
Frontend only; no result storage
No change — no result S3 bucket needed; results returned in response body
Synchronous response model avoids all S3 result-storage costs
Cost Tagging
No resource tags
Tag Lambda and S3 with Project=log-analyzer, Owner=jimmy-hubbard
Enables Cost Explorer breakdown as project count grows
Estimated Monthly
~$2.00–$5.00 at low volume
~$2.00–$5.00 at low volume (dominated by Anthropic API token cost)
CloudFront, Lambda, and S3 all stay in free tier; Anthropic API is the only variable cost
🌱 Pillar 6 — Sustainability
Area
Current
Production Design
Reasoning
Compute Model
Serverless Lambda — runs only during request, zero idle
No change — serverless is the most sustainable compute model
No EC2 running 24/7; compute consumed only when users actually analyze logs
Model Efficiency
Claude Haiku — one of Anthropic’s most efficient models
No change; prefer Haiku over larger models unless quality requires upgrade
Smaller models consume less compute per token — same output, lower carbon per analysis
Data Transfer
Log text travels us-east-1 → Lambda → Anthropic API (external HTTPS call)
Lambda stays in us-east-1; Anthropic API call is an external HTTPS request
Log text is the only external data transferred; results are returned in the same request
Right-Sizing
Default Lambda memory; may be over-provisioned
Benchmark optimal memory setting — right-size to avoid waste
Over-provisioned Lambda wastes compute resources on every invocation
📊 Current vs. Production Summary
Dimension
Current
Production
Effort
Frontend Transport
HTTP (S3 website)
HTTPS via CloudFront + ACM
Medium
Bucket Access
Public-read
Private + CloudFront OAC
Medium
Function URL Protection
None
WAF rate limit + payload size rule
Medium
IAM Scope
API key in Lambda env variable
No AWS IAM model permissions needed
Low
Deployment
Manual CLI
GitHub Actions CI/CD
Medium
Observability
Default CloudWatch Logs only
Structured JSON logs + custom metrics
Low
Error Handling
Unhandled exceptions → 500
Structured error responses + alarms
Low
Lambda Memory
128MB default
Benchmarked optimal size
Low
Log Chunking
Truncate at 50K chars
Token-aware chunking with partial results
Medium
Estimated Monthly Cost
~$0.10–$0.50
~$0.10–$0.50
No change
Production Architecture — Well-Architected Design
What I Learned
This was my first time connecting a frontend directly to a Lambda Function URL without API Gateway. Configuring CORS correctly, structuring requests to the Anthropic API, and keeping the flow synchronous gave me a solid understanding of how serverless functions handle real-time AI calls. I also learned how to craft prompts that consistently produce structured, useful outputs instead of generic text.
About: AI Resume ↔ Job Matcher
Job hunting is a numbers game — but most people apply blind. This tool makes the process smarter. Upload your resume and paste in a job description, and Claude does what a recruiter does in 10 seconds: compares the two, reasons through the fit, and gives you something actionable.
Claude returns a match score, a breakdown of missing skills you could address, and a list of strengths that directly align with the role. No fluff — just signal.
Project: AI Resume ↔ Job Matcher · Reviewed: 2026-03-19 · Architect: Jimmy Hubbard
Stack: S3 Static Website · HTML/CSS/JS · API Gateway (REST) · Lambda (Python 3.11) · Anthropic Claude API (claude-sonnet-4-6) · S3 Results Bucket Key fact: Async architecture — POST /analyze returns a job_id immediately. Lambda invokes itself with InvocationType=Event, runs pypdf + Claude API, stores JSON result in S3. Frontend polls GET /result/{jobId} every 2s until complete.
⚙️ Pillar 1 — Operational Excellence
Area
Current
Production Design
Reasoning
Deployment
Manual Lambda zip deploy + manual S3 upload
GitHub Actions: package Lambda layer (pypdf), deploy function code, sync frontend — all triggered by push to main
Multi-component deploys (frontend + Lambda + dependencies) are error-prone manually; CI/CD ties every change to a commit
Dependency Management
pypdf bundled in Lambda zip; manual version updates
Lambda Layer for pypdf — shared across function versions; Dependabot PR for version bumps
Layers allow dependency updates without repackaging the entire function
Rollback
Re-upload previous zip manually
Lambda versioned aliases (prod → stable version); one alias pointer update to rollback
Instant rollback without redeployment; traffic routing between versions possible for canary releases
Observability
Lambda CloudWatch Logs; API Gateway access logs disabled
Enable API Gateway execution + access logs; structured JSON from Lambda; CloudWatch dashboard: request rate, 4xx/5xx, async success rate, Claude API latency
Async architecture is harder to debug — structured logs with job_id correlation are essential
Dead-Letter Queue
None — failed async invocations silently lost
SQS DLQ on async Lambda destination; CloudWatch alarm fires if DLQ depth > 0
Without DLQ, a Claude API timeout or PDF parse error leaves the user polling forever with no result
🔒 Pillar 2 — Security
Area
Current
Production Design
Reasoning
API Key Exposure
Anthropic API key stored in Lambda environment variable (unencrypted by default)
Store API key in AWS Secrets Manager; Lambda retrieves at runtime via SDK call; rotate on schedule
Environment variable secrets are visible in console and CloudTrail; Secrets Manager adds encryption, audit trail, and rotation
API Gateway Auth
No auth — POST /analyze is open to anyone
API key required in request header (x-api-key); WAF rate limit: 20 requests/5 min per IP
Open endpoints can be abused for API cost attacks; Anthropic API calls are not free
Input Validation
PDF uploaded directly; no file-type validation or size limit enforced server-side
Lambda validates Content-Type, rejects files > 5MB, runs pypdf in try/except with meaningful error response
Malicious PDF uploads are a known attack vector; size limits prevent cost abuse
S3 Results Bucket
Public-read disabled but no explicit bucket policy
Explicit deny-all public access; ore-signed URLs for result retrieval instead of key-based polling
Key-based polling with predictable job_id is guessable; pre-signed URLs expire and are unforgeable
Transport
Frontend HTTP (S3 website); API Gateway HTTPS
CloudFront for frontend — HTTPS end-to-end
HTTP frontend breaks the security chain; MITM can intercept the job_id or inject fake results
IAM Roles
Lambda role scope unknown
Least-privilege: Lambda allowed only s3:PutObject on results bucket prefix, lambda:InvokeFunction on self ARN, secretsmanager:GetSecretValue on specific secret
Over-permissioned Lambda roles are the most common serverless security finding
🛡️ Pillar 3 — Reliability
Area
Current
Production Design
Reasoning
Async Failure Visibility
Failed async invocations are silent — user polls forever
DLQ captures failures; Lambda writes error JSON to results bucket so frontend polling gets a clear error state
The async pattern requires explicit failure signaling; silence is not an acceptable failure mode
Claude API Reliability
Single call; no retry logic
Exponential backoff with 3 retries on Claude API 529 (overloaded) errors; timeout set to 90s
Claude API can return 529 under load; retries with backoff recover automatically without user action
Personal project volume stays deep in free tier indefinitely
API Gateway Cost
First 1M calls/month free (REST API)
No change — stays in free tier at personal scale
Even 10,000 analyses/month × 50 polling calls each = 500K calls — within free tier
S3 Results Cost
Small JSON files stored indefinitely
24-hour lifecycle expiry; trivial storage cost even without expiry
Each result JSON is <10KB; 10,000 stored results = 100MB = ~$0.002/month
Estimated Monthly
~$0.03–$0.15 per 10 analyses (dominated by Claude API)
~$0.02–$0.12 per 10 analyses (output token cap reduces cost 20-30%)
Claude API is the only meaningful cost driver at personal-project scale
🌱 Pillar 6 — Sustainability
Area
Current
Production Design
Reasoning
Compute Model
Serverless Lambda — zero idle compute
No change — serverless is the correct sustainability choice
Lambda runs only during active requests; no EC2 consuming power between analyses
Model Efficiency
claude-sonnet-4-6 — strong balance of capability and efficiency
Evaluate claude-haiku-4-5 for simpler match tasks — 10x fewer compute resources per token
For structured scoring tasks, Haiku may produce equivalent quality at a fraction of the compute cost
Data Retention
Resume and JD content stored indefinitely in Lambda logs
Set CloudWatch log group retention to 7 days; enable S3 result expiry
PII in logs retained beyond necessity is both a privacy and sustainability concern — shorter retention = less storage energy
Regional Processing
All services in us-east-1; Anthropic API call goes to external network
No change — no AWS alternative to Anthropic API; keep all AWS resources in single region
Single-region eliminates cross-region data transfer energy; Anthropic API is the only necessary external call
📊 Current vs. Production Summary
Dimension
Current
Production
Effort
API Key Security
Lambda env var
Secrets Manager + rotation
Medium
API Auth
None (open endpoint)
API key + WAF rate limit
Medium
Frontend Transport
HTTP
HTTPS via CloudFront
Medium
Async Failure Handling
Silent (user polls forever)
DLQ + error result written to S3
Medium
Claude API Retry
None
Exponential backoff, 3 retries
Low
IAM Scope
Unknown / broad
Least-privilege per resource
Low
PDF Error Handling
Exception propagates silently
Structured error + fallback extraction
Low
Result Expiry
Indefinite
24-hour S3 Lifecycle rule
Low
Polling Efficiency
Fixed 2s interval
Exponential backoff polling
Low
Output Token Cap
None
1,000 token output limit
Low
Deployment
Manual
GitHub Actions CI/CD
Medium
Estimated Monthly Cost
~$0.03–$0.15 per 10 analyses
~$0.02–$0.12 per 10 analyses
Minor improvement
Production Architecture — Well-Architected Design
What I Learned
Getting consistent, structured comparisons from the AI required careful prompt engineering. I also focused more on the end user—someone likely stressed during a job search—which pushed me to make outputs clear, concise, and actionable rather than just technically correct.
About: Live Traffic Dashboard
A live traffic incident map across U.S. cities built with Leaflet.js and OpenStreetMap. Attempts the TxDOT 511 public API on every load, falling back to simulated demo data. Incidents are color-coded by type (accidents, construction, hazards, closures) and can be filtered and searched in real time from the sidebar.
The map auto-refreshes every 45 seconds. Clicking a sidebar card pans and zooms to that marker; clicking a marker highlights the card. Fully client-side — no backend required.
Project: Live Traffic Dashboard · Reviewed: 2026-03-24 · Architect: Jimmy Hubbard
Stack: S3 Static Website · HTML/CSS/JS · Leaflet.js · OpenStreetMap · Simulated incident data (demo mode) Key fact: Fully client-side — no backend. TxDOT 511 and SF Bay 511 API calls are CORS-blocked from S3. The app falls back to a 40-incident simulated data pool that churns on every refresh. Lambda proxy for live data is deferred until CloudFront HTTPS is active.
⚙️ Pillar 1 — Operational Excellence
Area
Current
Production Design
Reasoning
Deployment
GitHub Actions pushes index.html to S3 on every merge to main
Same pipeline extended: after S3 sync, invalidate CloudFront distribution to flush cached HTML
Without a cache invalidation step, users may see a stale version for up to 24 hours after deployment
Rollback
Re-push previous commit; Actions redeploys
Enable S3 Versioning — restore prior object version in one CLI command without needing a full re-deploy
Versioning decouples rollback from the git pipeline; useful when the pipeline itself is the problem
Observability
No logging — fully client-side, no server to instrument
CloudFront access logs to S3; Lambda proxy logs structured JSON to CloudWatch (source attempted, latency, status code)
CloudFront logs reveal 4xx/5xx rates and geographic traffic patterns; Lambda logs expose TxDOT API reliability over time
Health Check
No external monitoring
UptimeRobot monitors the CloudFront URL every 5 minutes; alert on HTTP non-200
Detects S3/CloudFront outages before users report them
🔒 Pillar 2 — Security
Area
Current
Production Design
Reasoning
Transport
HTTP only — S3 static website does not support HTTPS
CloudFront distribution with ACM certificate — HTTPS enforced, HTTP redirected; S3 bucket made private with OAC
HTTP exposes users to MITM and triggers browser security warnings; private S3 with OAC closes the direct-access bypass
Bucket Access
Public-read bucket policy required for S3 website hosting
Block all public access; CloudFront Origin Access Control (OAC) is the only authorized principal
Public-read buckets are a top misconfiguration finding; OAC enforces that all requests go through CloudFront
Lambda Proxy Auth
N/A — no backend yet
Lambda Function URL with AuthType NONE; WAF rule group applies IP rate limit (60 req/min per IP) to prevent abuse
Open Function URLs without WAF are trivially abusable for cost amplification attacks
API Key Storage
N/A — TxDOT 511 public endpoint used directly (no key required currently)
If TxDOT adds API key requirement: store in AWS Secrets Manager; Lambda retrieves at cold-start and caches in memory
Never hardcode API keys in Lambda environment variables — Secrets Manager provides rotation and audit trail
XSS Prevention
All incident fields passed through esc() helper before insertion into DOM
Maintain esc() on all data-driven HTML; add Content-Security-Policy header via CloudFront response policy
CSP header provides defense-in-depth against injected scripts even if an esc() call is missed
🔁 Pillar 3 — Reliability
Area
Current
Production Design
Reasoning
Data Fallback
3-source cascade: TxDOT → SF Bay 511 → Simulated demo; demo always succeeds
Same cascade maintained via Lambda proxy; Lambda itself returns cached last-good data if TxDOT API is unreachable for > 2 consecutive calls
TxDOT 511 has unpredictable uptime; stale-cache fallback ensures the map always shows something meaningful
CDN Dependency
Leaflet.js loaded from unpkg.com CDN on every page load
Self-host Leaflet assets in the S3 bucket alongside index.html; served via CloudFront
Third-party CDN outage (unpkg.com) currently takes down the entire map; self-hosting eliminates this single point of failure
Refresh Resilience
45-second auto-refresh; failed refreshes silently keep showing last data
Add exponential backoff (45s → 90s → 180s) after consecutive Lambda errors; toast notification when data is stale
Silent failures confuse users; backoff reduces hammering a degraded backend
Availability
S3 website: 99.9% SLA; no multi-region
CloudFront edge caching provides effectively global availability even during brief S3 origin hiccups
Static assets cached at CloudFront edge means S3 origin can be briefly unavailable with no user impact
⚡ Pillar 4 — Performance Efficiency
Area
Current
Production Design
Reasoning
Asset Delivery
Single 30KB HTML file served from S3 us-east-1 origin
CloudFront edge delivery — users get the file from the nearest PoP; typical cache-hit latency < 10ms
S3 origin delivery from us-east-1 adds 50–200ms round-trip for users outside the east coast
Map Tiles
OpenStreetMap tiles loaded on demand; free, no SLA
Evaluate MapTiler or Stadia Maps for a tile CDN with SLA and higher rate limits as traffic grows
Python Lambda, minimal dependencies — cold start target < 400ms; provisioned concurrency not needed at personal-project scale
Cold starts only matter on first request after idle; 45-second refresh cadence keeps Lambda warm during active sessions
Bundle Size
All CSS + JS inline in 30KB HTML; Leaflet loaded separately from CDN
Keep inline bundle; compress with gzip via CloudFront — typical savings 60–70% on a JS-heavy file
CloudFront gzip compression is one checkbox; no build step required for a single-file app
💰 Pillar 5 — Cost Optimization
Area
Current
Production Design
Reasoning
S3 Hosting
~$0.00/month — single 30KB file, minimal requests
No change — S3 costs negligible at personal-project scale
S3 storage is $0.023/GB; a 30KB file costs essentially $0
Lambda Proxy
N/A
Lambda free tier: 1M requests/month + 400,000 GB-seconds compute free. At 45s refresh per user, 100 daily active users = ~192K req/month — well within free tier
Lambda proxy is effectively free at personal portfolio scale; no cost concern
CloudFront
N/A — HTTP S3 only
CloudFront free tier: 1TB data transfer + 10M HTTP requests/month. 30KB file × 10K monthly visits = ~0.3GB — well within free tier
CloudFront adds HTTPS, CDN speed, and OAC security at $0 for this traffic level
Map Tiles
OpenStreetMap: free
Keep OpenStreetMap for portfolio use (low traffic); monitor tile request rate as usage grows
OpenStreetMap is free for low-traffic non-commercial use; paid alternative only needed at scale
Production Architecture — Well-Architected Design
What I Learned
Public APIs are often far messier than their documentation suggests. Designing a fallback cascade—live feed → secondary source → simulated data—taught me how to build resilient data pipelines that don’t fail when external sources do. Using Leaflet.js for real-time map rendering also pushed me to think more critically about performance as data updates.