E2E encrypted, serverless, ephemeral full-stack file-drop demo on AWS. Upload a file in the browser and get a link; open the link elsewhere to download and decrypt. Built with React + Vite; infra and deployment via Terraform and the Makefile.
After cloning, run help to see commands, then install and run the app:
/ghostdrop$ make help
┌─ Prerequisites ──────────────────────────────────────────────┐
│ make install Install app dependencies (for local) │ <-- run this once
│ make terraform-init Initialize Terraform (for publish only) │
└──────────────────────────────────────────────────────────────┘
┌─ Local (No backend; use browser storage) ────────────────────┐
│ make local Run web app in dev mode (hot reload) │ <-- then this
└──────────────────────────────────────────────────────────────┘
┌─ Remote deploy (Backend: API + S3 + CloudFront + WAF*) ──────┐
│ make publish Build SPA with API base, deploy │
│ make unpublish Destroy deployed infra │
│ │
│ *WAF: Nordic whitelist (FI, SE, NO, DK, IS) on CloudFront │
└──────────────────────────────────────────────────────────────┘Run make install once, then make local. The app opens at http://localhost:5173. Constants and commands are defined in the Makefile.
- Local (make local): Node.js 20.19+ (see
app/.nvmrc). Use nvm if needed:nvm installthennvm use. - Deploy (make publish / make unpublish): Node.js 20.19+, Terraform, AWS CLI, and AWS credentials (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_REGION).
Deployed stack:
- S3 (SPA) — Static site. No public access; only CloudFront via Origin Access Control (OAC). Unknown paths →
index.htmlfor fragment routing. - S3 (uploads) — Encrypted blobs. No public access; access only via presigned URLs from the Lambdas. Lifecycle: delete after 24h; abort incomplete multipart uploads after 24h. Keys:
uploads/<uuid>. - CloudFront — In front of the SPA bucket. Security headers (CSP, X-Frame-Options, X-Content-Type-Options). Optional WAF (Nordic whitelist). Caching: default TTL;
make publishinvalidates so the new build is visible. - Lambda (upload signer) — Presigned POST for the uploads bucket. Enforces max size and path; short-lived. Via API Gateway.
- Lambda (download signer) — Presigned GET for a given object ID (and size check). Via API Gateway.
- API Gateway (HTTP API) —
POST /upload-urlandPOST /download-url. CORS restricted to CloudFront origin; rate limits.
Browser (SPA)
|
1. Load app → CloudFront → S3 (SPA)
|
2. POST /upload-url → API Gateway → upload-signer → presigned POST → SPA
3. SPA POSTs file → S3 (uploads)
|
4. POST /download-url → API Gateway → download-signer → presigned GET → SPA
5. SPA GETs file → S3 (uploads)
Upload flow: User opens the CloudFront link. They pick a file; the browser encrypts it (AES-GCM, random key and nonce). The SPA gets a presigned POST from the upload-signer Lambda via API Gateway, then POSTs the encrypted bytes to the uploads bucket. The SPA builds a download link whose fragment holds the object ID, decryption key, nonce, and filename. Key and nonce stay in the fragment and are never sent to the server.
Download flow: User opens a link with a fragment. The SPA parses it (key, nonce, object ID, name), calls the download-signer Lambda for a presigned GET, fetches the ciphertext from S3, decrypts in the browser, and saves. Files in the bucket expire after 24h.
| Path | Description |
|---|---|
app/ |
React SPA (Vite). Entry: app/index.html, app/src/main.tsx. |
terraform/ |
Infra (S3, Lambdas, API Gateway, CloudFront, WAF). Constants in the Makefile are passed via TF_VAR_* and VITE_*. |
design_docs/ |
Design and requirements. See Documentation below. |
- initial_plan.md — Product and infra overview: components, flows, deployment goal, Lambdas, S3, security, notes.
- spa-design.md — SPA design: crypto, URL fragment format, upload/download flows, API contract, UX, constraints.
State is stored locally (e.g. terraform/terraform.tfstate). No remote backend or state locking. The project is meant to be deployed and torn down at will; a single operator can reproduce the stack from this repo and AWS credentials.
- Geoblocking — WAF Nordic whitelist (FI, SE, NO, DK, IS) on CloudFront; see Makefile help. Affects only the SPA; API and S3 are not behind WAF.
- Budget notifications — AWS Budget + SNS when cost exceeds a threshold.
