Building TexFolio: An AI-Powered LaTeX Resume Builder with Hono, LangGraph & BullMQ TexFolio, an open-source AI-powered LaTeX resume builder, compiles real pdflatex documents in the background to produce typography-perfect, ATS-friendly resumes. The backend runs on Hono v4 with MongoDB Atlas, Redis, BullMQ for async PDF jobs, Clerk auth, and role-based access control, while a multi-agent LangGraph pipeline scores resumes across Content, ATS, Format, and Impact. TexFolio is an open-source, AI-powered LaTeX resume builder. Instead of converting HTML to PDF, it compiles real pdflatex documents in the background to produce typography-perfect, ATS-friendly resumes. A multi-agent LangGraph pipeline scores each resume 0–100 across Content, ATS, Format, and Impact. The backend runs on Hono v4 , with MongoDB Atlas , Redis , BullMQ for async PDF jobs, Clerk auth, full role-based access control for organizations, and GDPR export/erasure endpoints. Most resume builders generate HTML and convert it to PDF. The result is inconsistent spacing, fragile layouts, and output that Applicant Tracking Systems ATS struggle to parse. TexFolio takes a different path: it compiles real LaTeX with pdflatex , the typesetting standard used in academia and publishing. The output is consistent, clean, and ATS-friendly by design. The hard part is running LaTeX compilation safely, at scale, inside a web app, and that is where most of the engineering lives. TexFolio is a Turborepo monorepo with clear separation between the React frontend, the Hono API, and infrastructure services. CLIENT React 19 + Vite + Tailwind v4 + Zustand + React Query │ HTTPS Clerk JWT / API Key ▼ HONO v4 API SERVER Request ID → Logger → CORS + SecHeaders → Tiered Rate Limit → Input Sanitizer │ Routes: /resumes /ai /agents /organizations /payments /me │ Auth Clerk → RBAC requireRole → API Key HMAC → Audit Trail │ ▼ MongoDB Atlas • Redis • BullMQ Workers • External APIs │ NVIDIA NIM, Gemini, Groq, Clerk, Razorpay, Brevo ▼ pdflatex Docker or local The repo is organized as: apps/api — Hono v4 backend services, models, queues, agents, middleware apps/web — React 19 frontend features, hooks, stores, pages apps/latex-renderer — Dedicated Docker container for pdflatex packages/shared — Zod schemas and TypeScript types as a single source of truthThe API runs on Hono v4 , a Web Standards framework chosen over Express and Fastify for speed and a middleware-first design. Every request flows through a strict, ordered pipeline: requestIdMiddleware — assigns an X-Request-ID nanoid for end-to-end tracing structuredLogger — JSON logs with correlation IDs secureHeaders — CSP, X-Frame-Options, nosniff, referrer policy cors — origin whitelist with credentials tieredRateLimiter — Redis-backed limits Pro: 300/min, Free: 60/min, Anonymous: 20/min inputSanitizer — XSS and prototype-pollution preventionRoute-level middleware then layers on authMiddleware Clerk JWT , requireRole RBAC , and apiKeyMiddleware HMAC . Running pdflatex on arbitrary user input is a security and reliability minefield. TexFolio solves it in layers. LaTeX compilation is offloaded from the HTTP thread into a BullMQ queue backed by Redis. The client enqueues a job and polls for progress 10% → 30% → 100% . { concurrency: 2, // Max parallel compilations limiter: { max: 5, duration: 60000 }, // 5 jobs/min defaultJobOptions: { attempts: 3, backoff: { type: "exponential", delay: 2000 }, // 2s → 4s → 8s removeOnComplete: { count: 100 }, removeOnFail: { count: 50 }, } } The endpoints reflect this: POST /api/resumes/:id/pdf/queue returns a jobId , GET .../pdf/queue/:jobId polls status waiting → active → completed | failed , and .../download returns the binary. A synchronous GET /api/resumes/:id/pdf also exists for direct compilation. spawn , never exec Command injection is prevented by passing arguments as an array instead of concatenating strings: // SECURE: no shell interpretation spawn "docker", "exec", "texfolio-latex", "pdflatex", "-interaction=nonstopmode", filename ; // INSECURE never used : exec pdflatex ${filename} — "; rm -rf /" disaster All user input is escaped before template rendering \ , & , % , $ , , , { , } , ~ , ^ to prevent LaTeX injection. Template IDs and filenames are sanitized against path traversal using path.basename and regex whitelisting. SIGKILL for hung compilations << delimiters so templates cannot execute codeTemplates are real .tex files: classic.tex , faangpath.tex , and premium.tex . The "Resume Coach" is a LangGraph state machine, not a single prompt. It runs five sequential nodes: START → content → ats → format → impact → synthesize → END | Node | Purpose | Weight | |---|---|---| content | Content quality analysis | 30% | ats | ATS keyword compatibility | 25% | format | Structure / layout review | 20% | impact | Overall effectiveness | 25% | synthesize | Weighted final score + recommendations | — | Each node creates an LLM instance, sends the resume data, requests JSON-only output, and returns a partial state update. Critically, the pipeline never halts on a single failure : if a node cannot parse the LLM output, it returns a zero score and moves on. You hit it via POST /api/agents/coach , which returns a finalScore , per-category analysis, and a recommendations array. Other AI endpoints include /api/ai/improve , /api/ai/generate-bullets , /api/ai/cover-letter , and /api/agents/import/linkedin parses a LinkedIn PDF export into structured resume data . Reliability comes from a priority-based provider chain wrapped in a circuit breaker: NVIDIA NIM Llama 3.1 70B → Google Gemini 1.5 Flash → Groq Llama 3.1 70B The circuit breaker CLOSED → OPEN → HALF OPEN trips after 5 consecutive failures, waits 30 seconds, then tests recovery with 2 successes before closing. Its live state is exposed at GET /health/ai . TexFolio supports teams with a strict role hierarchy enforced by weight comparison: Owner 4 → Admin 3 → Editor 2 → Viewer 1 Organization context is resolved from an X-Organization-Id header injected by an Axios interceptor on the frontend or a route param. The requireRole "admin" middleware compares role weights and returns 403 when insufficient. Resumes marked visibility: "organization" are shared across the team, and PDF generation automatically applies the org's locked template, primary color, and font overrides. Ownership transfer is atomic: promoting a member to owner automatically demotes the previous owner to admin , and both changes are written to the audit trail.