Projects About Capabilities Get in Touch
Back to portfolio
Case Study

RabbitHole

A full-stack video platform for uploading, processing, searching, and streaming media. The React experience stays connected to the backend throughout the workflow, showing live processing state before turning video, transcripts, metadata, and semantic matches into something users can actually browse and search.

FrontendReact + hls.js
BackendFastAPI + WebSockets
CloudEvent-driven AWS
Architecture note: this case study documents an earlier RabbitHole iteration that I implemented and deployed. The live product has continued to evolve, so the current interface and behavior may not map one-for-one to every component described below. The repository preserves the infrastructure and delivery evidence for this architecture.

Built as an end-to-end product, not just an infrastructure exercise: a React playback and search experience, a FastAPI application layer, real-time status over WebSockets, AI-assisted metadata and speech indexing, and an asynchronous media-processing pipeline underneath it. The AWS architecture and Terraform support the product rather than being the product.
RabbitHole application interface

The problem

The product problem is bigger than transcoding a file. A user needs to upload media, understand what is happening while it processes, play the finished video smoothly, and find useful moments later without manually scrubbing through hours of footage. RabbitHole connects those user-facing workflows to a long-running asynchronous backend without making the interface feel disconnected from the work happening behind it.

What it does

Architecture

An event-driven pipeline that decouples the fast path (upload) from the slow path (transcode), with a serverless API and a container worker fleet, using the right tool for each job. The AI work hangs off that same backbone: synchronous vision at the worker, async event-driven speech-to-text, and an embedding model inside the API.

System architecture

From upload to playback

Fast requests stay serverless. Heavy media work moves through a queue to container workers.

01

Ingest

Browser
API Gateway + Lambda
S3 uploads

Presigned uploads keep video bytes off the API path.

02

Orchestrate

EventBridge
SQS
DLQ + retries

Upload events are buffered so processing can scale independently.

03

Process

ECS Fargate
ffmpeg
Claude Vision + Transcribe

Long running media work and AI enrichment happen off the request path.

04

Deliver

S3 HLS
CloudFront
WebSocket status

Adaptive streams are delivered from the edge while status updates stay live.

Platform foundation
StateDynamoDB
ObservabilityCloudWatch
InfrastructureTerraform
DeliveryGitHub Actions + OIDC

Highlights

How it ships & runs

The operational layer is part of the project, not an afterthought; it's what turns "a deployed app" into something you can actually run and evolve.

Engineering decisions & trade-offs

Lambda API + Fargate workers

Right tool per job: serverless for the lightweight, bursty API; containers for the long-running, CPU-heavy ffmpeg transcode that would never fit Lambda's runtime and size limits.

AWS Transcribe over self-hosted Whisper

Managed and event-driven (job-complete → EventBridge → Lambda), with native word-level timestamps. It keeps the worker lean and the transcode path fast; the trade is per-minute cost versus running a model in-container.

A local embedding model, not a managed vector DB

Search embeds with a small ONNX model baked into the API and runs brute-force cosine over vectors in DynamoDB. At portfolio scale that's instant and costs nothing; OpenSearch or pgvector would be the move at a far larger corpus, but that would also reintroduce idle cost the rest of the system works hard to avoid.

Direct-to-S3 upload (presigned)

The API issues a presigned URL and the browser uploads straight to S3, so the API never proxies file bytes; this is cheaper, faster, and far friendlier to a Lambda execution model.

No NAT gateway (a documented cost trade-off)

Workers run in public subnets with a zero-ingress security group instead of private subnets behind a NAT gateway. That keeps idle cost near zero for a demo; the production trade-off is noted below.

What I'd change at scale

The demo deliberately optimizes for cost and clarity. Honest production trade-offs that remain:

My role: Sole architect and engineer: the event-driven AWS architecture, the FastAPI/Lambda API, the Fargate ffmpeg worker, the AI metadata + speech-to-text + semantic-search pipelines, the real-time WebSocket layer, the React + hls.js frontend, the OIDC-based CI/CD and observability, and the Terraform that provisions all of it.