FaceID is a serverless private event-gallery app for photographers and event teams that groups uploaded event photos by recognizable guests inside event-scoped workspaces. The React frontend supports event creation, consent-attested guest reference intake, event photo uploads, human review decisions, per-person galleries, and owner-controlled deletion. The production architecture is designed around Cloudflare Pages for the static app and a Cognito-authenticated AWS serverless backend for private storage, Rekognition-based face comparison, and DynamoDB metadata.
Live demo: https://faceid-8dc.pages.dev
The hosted demo is connected to the Terraform-managed AWS backend. The app still supports local mock mode when VITE_API_BASE_URL is unset.
This project demonstrates a pragmatic path from an interactive frontend prototype to a deployable serverless image-processing system for private event photo delivery. The UI is usable without a backend for review/demo purposes, while the AWS backend is defined in Terraform so the infrastructure can be created and destroyed repeatably.
- Frontend: React 19, TypeScript, Vite
- UI: CSS modules-by-convention in
src/styles.css,lucide-reacticons - Hosting: Cloudflare Pages, Wrangler
- Backend: AWS API Gateway HTTP API, Cognito Hosted UI/JWT authorizer, Python 3.12 Lambda
- Storage: Private S3 bucket with presigned PUT and GET URLs
- Face matching: Amazon Rekognition
IndexFacesfor references and boundedCompareFaceschecks for uploaded photos - Database: DynamoDB tables for events, people, photos, matches, and short-lived upload sessions
- Infrastructure: Terraform with cost guardrails for throttling, batch size, and upload limits
- Quality: Vitest, Python
unittest, GitHub Actions CI, Terraform validation
- Direct browser-to-S3 uploads through presigned URLs, avoiding API Gateway payload limits for image files.
- Dual runtime mode: mock data for local/frontend review, real AWS API mode when
VITE_API_BASE_URLis set. - Cognito sign-in with API Gateway JWT authorization and owner-scoped S3/DynamoDB records.
- Event/workspace data model that scopes people, photos, matches, and uploads to a selected private event.
- Consent-attested reference intake stores consent status/source metadata with guest profile records.
- Human-in-the-loop review workflow for match candidates:
matched,needs_review,approved, andrejected. - DynamoDB-backed upload sessions that verify issued S3 keys, object size, content type, and upload metadata before processing.
- Owner-scoped delete flows for removing uploaded photos and reference/person records from S3, DynamoDB, and Rekognition.
- Event-gallery UX focused on guest reference intake, event photo upload, review queue triage, and per-person galleries.
- Focused frontend and backend tests for API upload contracts, auth context, and upload validation.
- GitHub Actions CI for linting, tests, frontend build, Lambda syntax, and Terraform validation.
- Structured Lambda logs, API Gateway access logs, CloudWatch alarms, and optional AWS Budget alerts.
- Reference-photo naming flow that derives people from filenames such as
jane-smith.jpg. - Matching-quality guidance for adding multiple guest references when side angles, candid photos, or poor lighting are expected.
- Private photo storage with short-lived signed preview URLs rather than public S3 objects.
- Explicit low-volume cost controls: max files per batch, upload size limits, bounded people/reference comparisons, API throttling, and short CloudWatch log retention.
- Terraform-managed backend resources with
terraform destroysupport for cleanup. - Clear match states for responsible review:
matched,needs_review,approved,rejected, andunknownin the shared API types.
The system is documented in:
- Architecture Overview
- Backend API Contract
- Responsible Use And Matching Quality
- Architecture Decision Records
- Terraform Deployment Notes
At a high level:
Browser -> Cloudflare Pages React app -> Cognito + AWS API Gateway -> Lambda
|-> S3
|-> Rekognition
|-> DynamoDB
npm install
npm run devThe app runs in mock mode until VITE_API_BASE_URL is set.
npm run lint
npm test
npm run build
python3 -m py_compile backend/lambda/app.py
terraform -chdir=infra/terraform fmt -check
terraform -chdir=infra/terraform validate
git diff --check- Framework preset:
Vite - Build command:
npm run build - Build output directory:
dist - Node version:
22
Set these Cloudflare Pages environment variables after the AWS API is deployed:
VITE_API_BASE_URL=<api_base_url>
VITE_AUTH_CLIENT_ID=<cognito_web_client_id>
VITE_AUTH_DOMAIN=<cognito_hosted_ui_domain>
Terraform lives in infra/terraform and creates the serverless backend:
cd infra/terraform
terraform init
terraform applyDestroy everything, including uploaded photos:
cd infra/terraform
terraform destroySee infra/terraform/README.md for variables, cost guardrails, and Cloudflare CORS setup.
- Uploaded photos are stored in a private S3 bucket.
- The frontend receives short-lived signed URLs for uploads and previews.
- Cognito protects API routes with JWT authorization.
- Lambda scopes S3 keys and DynamoDB reads/writes to the authenticated Cognito user.
- Upload processing requires a valid upload session and verifies the S3 object before invoking Rekognition.
- Reference uploads require explicit consent confirmation in the UI and store consent metadata with the person record.
- Users can remove their uploaded photos and reference/person records; deletes remove private S3 objects and related metadata.
- Face matching is probabilistic; review lower-confidence matches before using a gallery outside personal photo organization.
- Upload sessions are marked
processedorfailedso partial processing failures are visible in backend state. - The public Cloudflare demo is connected to AWS with conservative API throttles and upload limits.
force_destroy_bucketdefaults totruein Terraform so teardown removes uploaded assets.
- The MVP backend uses bounded
CompareFaceschecks against stored reference images; this is simple and deployable, but cost grows withphotos * people * references. - Different face angles and poor image quality can reduce match confidence; add more guest reference photos and use the review queue for use cases where this matters.
- The current Lambda does not crop every face in group photos before searching. Large group-photo support would require a deeper face-detection/cropping pipeline.
- No moderation, guest self-service invite links, share links, role-based admin workflow, or retention scheduler is included.
- Correctional surveillance, public-space tracking, inmate tracking, and other law-enforcement identification workflows are explicitly non-target use cases for this project.
- Reference images are managed at the person level in the current UI rather than as individually editable reference assets.
- The current frontend does not refresh Cognito tokens in place; users sign in again after the token expires.
- CI validates infrastructure and app code, but deployment is still intentionally manual.
- The Cloudflare Pages deployment is static; backend environment wiring happens through
VITE_API_BASE_URL,VITE_AUTH_CLIENT_ID, andVITE_AUTH_DOMAIN.
backend/lambda/ Python Lambda API
.github/workflows/ CI validation workflow
docs/ API, architecture, and ADR documentation
infra/terraform/ AWS serverless infrastructure
public/ Cloudflare Pages headers and redirects
src/ React frontend