PixLume — AI-based Vision Labelling Platform
VISIT_PROJECTI built PixLume — a full-stack AI platform for labelling computer vision datasets. Upload images, draw bounding boxes, get AI-powered suggestions, review annotations, and export clean datasets. The whole pipeline, zero infrastructure cost.
The Stack
It's a monorepo with npm workspaces:
| Component | Tech | Deployment |
|---|---|---|
apps/web | Next.js 16 | Vercel |
apps/api | Fastify | Render |
apps/worker | BullMQ | Render (alongside API) |
packages/types | Shared TypeScript types | — |
Infrastructure — entirely free-tier:
- Neon — PostgreSQL (Prisma ORM)
- Upstash — Redis (powers BullMQ job queue)
- Cloudflare R2 — Image storage (S3-compatible, zero egress fees)
- Vercel — Frontend hosting
- Render — API + worker hosting
Zero dollars. The whole thing runs on air.
How It Works
The Flow
You create a project — a container for a dataset. Each project has its own annotation classes (car, person, dog, etc.).
Upload images → they go straight to R2 → the BullMQ worker generates thumbnails in the background. Open the annotation editor — a canvas-based tool built with react-konva — and draw bounding boxes.
AI Suggestions
Instead of drawing every box by hand, hit a button and the platform runs YOLOv2.6 inference on the image. It suggests bounding boxes that you can accept, adjust, or reject.
Review Flow
After annotating, images go through a review pipeline — approve or reject with comments. Critical for team-based annotation with quality control.
Export
Export datasets in standard formats (YOLO, COCO, etc.) as zip files, ready for training.
Security Architecture
The frontend-to-backend communication goes through an SSR proxy — a Next.js catch-all Route Handler that forwards requests server-to-server.
- JWT tokens live in httpOnly cookies — JavaScript can't touch them. No XSS can steal your session.
- Token refresh is transparent — if the API returns 401, the proxy automatically refreshes and replays the request.
- No API URLs leak to the browser — the frontend only talks to
/api/*on its own domain. - File downloads are proxied — export files stream through the API instead of exposing R2 presigned URLs.
Full CSP header setup: X-Frame-Options: DENY, HSTS with preload, strict connect-src.
Image Proxy
Images in R2 aren't served directly to the browser. An image proxy route on the API fetches from R2 and streams the bytes through — R2 credentials never touch the frontend.
Upload Flow
- Presigned URL upload — time-limited signed URL, browser uploads directly to R2
- Direct upload — file goes through the API proxy (simpler CORS for smaller files)
Both support batch uploads with automatic thumbnail generation via the BullMQ worker.
The Data Migration Adventure
The original setup ran everything in Docker — PostgreSQL, MinIO, Redis on a single server. Moving to the cloud stack was an experience.
Database: Dumped with pg_dump, but Neon doesn't let you disable foreign key constraints during import. Had to manually reorder all COPY statements in dependency order: users → organizations → memberships → projects → classes → images → annotations.
Images: MinIO stores objects in a custom internal format (xl.meta binary files) — can't just copy files. Ran a migration script that pulled each object through the MinIO API and re-uploaded to R2.
Key Takeaways
- Free tier is surprisingly capable. Neon + Upstash + R2 + Vercel + Render gives you a real production stack for $0.
- ONNX Runtime is underrated. Replacing a Python ML service with a single JS library call was one of the best decisions.
- SSR proxies solve so many security problems. httpOnly cookies + server-side forwarding eliminates entire classes of attacks.
- Data migrations are always harder than you think. Especially with foreign keys and proprietary storage formats.
- Git LFS for ML models just works. 213MB ONNX file, tracked properly, pulled at build time on Render.