Vault is a premium, end-to-end encrypted photo storage web app. Every photo is encrypted in your browser with AES-256-GCM before it ever touches the network. The server and the S3 bucket only ever hold opaque ciphertext — there is no key on the server, no backdoor, and no way for anyone (including the operator) to view your photos.
Your password is your encryption key. It never leaves your device.
|
🔐 True zero-knowledge Client-side AES-256-GCM. The backend stores encrypted blobs and metadata only. 🗝️ Password-derived keys Keys derived with PBKDF2 · 310,000 iterations · SHA-256. Never transmitted. ⚡ Direct-to-S3 transfers Presigned URLs mean encrypted data flows straight to S3 — the API never proxies your files. |
🖼️ Albums & gallery Organize into albums, view in a responsive grid, open a full-screen viewer. 📊 Storage metering Live usage tracking against a 1 GB per-user quota. 🎬 Crafted UI A cinematic gold-on-black aesthetic with Framer Motion transitions throughout. |
Landing — a cinematic gold-on-black entry, end-to-end encrypted from the first click.
The server never sees a raw photo or an encryption key. Forget your password and the data is unrecoverable — that's the point.
| Layer | Technology |
|---|---|
| Frontend | Next.js 14 (App Router), React 18, TypeScript |
| Styling | Tailwind CSS · Framer Motion · Cormorant Garamond |
| State/Data | Zustand · TanStack Query · Axios |
| API | Express · TypeScript · Zod validation |
| Database | PostgreSQL · Prisma ORM |
| Storage | AWS S3 (encrypted blobs, presigned URLs) |
| Crypto | Web Crypto API — AES-256-GCM · PBKDF2 (310k) |
| Auth | JWT access + refresh tokens · bcrypt · Helmet · rate limiting |
| Tooling | Turborepo monorepo · Docker Compose (Postgres + LocalStack) |
vault/
├── apps/
│ └── web/ → Next.js 14 web app (UI, client-side encryption)
├── packages/
│ └── core/ → Shared encryption, API client & TypeScript types
├── backend/ → Express API · Prisma · S3 (auth, photos, albums)
│ └── prisma/ → PostgreSQL schema (User · Photo · Album)
└── docker-compose.yml → Local Postgres + LocalStack (S3)
Prerequisites — Node.js 18+ · Docker & Docker Compose · (an AWS S3 bucket for production)
# 1 · Clone & install
git clone https://github.com/Tushar-Surti/Vault.git
cd Vault
npm install
# 2 · Start Postgres (+ LocalStack S3) locally
docker-compose up -d
# 3 · Configure the backend
cp backend/.env.example backend/.env
# set DATABASE_URL, JWT_SECRET, JWT_REFRESH_SECRET, AWS_* and S3_BUCKET_NAME
# 4 · Create the database schema
cd backend && npx prisma db push && npx prisma generate && cd ..
# 5 · Configure the web app
cp apps/web/.env.local.example apps/web/.env.local
# 6 · Run web + API together
npm run dev:all| Service | URL |
|---|---|
| Web app | http://localhost:3000 |
| API | http://localhost:4000 |
- Client-side encryption — photos are encrypted in the browser before upload.
- AES-256-GCM — authenticated encryption; tampering is detected on decrypt.
- PBKDF2 · 310,000 iterations — hardens password-derived keys against brute force.
- Zero-knowledge server — only ciphertext + metadata are stored; no keys, ever.
- Presigned URLs — encrypted data moves directly between browser and S3.
- Hardened API — Helmet security headers, CORS allow-list, and auth rate limiting (5 attempts / 15 min).
⚠️ There is no password reset. Your password is your encryption key — lose it and your photos cannot be recovered by anyone. That is the guarantee.
| Piece | Platform | Notes |
|---|---|---|
| Web app | Vercel | Next.js build; points at the hosted API |
| API | Render | Express server behind a reverse proxy (trust proxy enabled) |
| Database | Postgres | Any managed Postgres (Render / Supabase / RDS) |
| Storage | AWS S3 | Bucket with block all public access = ON + CORS allow-list |




