Projects
Backend systems, AWS architectures, voice platforms, and integration frameworks I've designed, shipped, and operated. Each case study covers context, approach, and outcome.
Self-hosted stack behind Traefik + Authentik
My living homelab: Docker Compose, Traefik v3, Step-CA for internal certs, Authentik forward-auth, 18+ services. Weekend project that somehow became production for my household.
Next.js 14 app with a RAG assistant, LLM i18n pipeline, and multilingual PDF resume
## Problem A resume asserts seniority; it can't demonstrate it. I wanted a portfolio that is itself a production system — every feature a verifiable work sample a recruiter can click, read, and interrogate. ## My role Everything: product, design, backend, frontend, and operations — designed, built, and run solo. ## Constraints - Serverless deployment must not exhaust Postgres connections, and public pages must stay ISR-cacheable behind strict security headers. - Content ships in English, Spanish, and German without triple-authoring every post. - The AI assistant answers from my real content only, within a daily cost budget, and has to resist prompt injection. ## Architecture Next.js 14 App Router with TypeScript and Prisma on Postgres. The assistant is retrieval-augmented: site content is chunked and embedded into pgvector, retrieved per question, and answered with OpenAI models — with prompt-injection defenses, per-IP rate limits, a daily token budget, and cron-driven re-indexing. An LLM translation pipeline generates the Spanish and German locales with hash-based staleness detection. The admin surface sits behind NextAuth credentials plus WebAuthn passkeys under a nonce-based CSP. The resume PDF is rendered server-side with PDFKit in all three languages. Blog search uses Postgres full-text search; every route emits canonical/hreflang metadata, JSON-LD, per-route Open Graph images, a sitemap, RSS, and llms.txt. ## Outcome The site you are reading: multilingual blog and case studies, an AI assistant grounded in my own writing, and a recruiter-ready PDF resume — all generated from one codebase and operated in production. ## What I'd do differently Design the i18n routing from the start — retrofitting it meant every public page now exists in two route trees. And put project metrics in the schema instead of prose so they can render as highlight chips.
One Factory Method backbone for Slack, Teams, Google Chat, and Webex at Espressive
## Problem Each enterprise chat channel — Slack, Microsoft Teams, Google Chat, Webex — started life as a bespoke integration. When fulfillment code branches on channel, you are maintaining N products that drift apart under deadline pressure. ## My role Designed and implemented the Factory Method–based integration framework and the shared testing utilities that every channel adapter builds on. ## Constraints - Channels differ in authentication models, payload shapes, and rate limits — the differences had to stay inside the adapters. - Existing live integrations had to keep working while the framework replaced them underneath. - Secure-by-default patterns (authentication, MFA, secrets handling) were table stakes for enterprise customers. ## Architecture Channel adapters normalize every inbound message into a common envelope; a Factory Method instantiates the right adapter per channel; fulfillment code operates on intents and entities and never branches on channel. Shared testing utilities give each new adapter the same conformance suite from day one. ## Outcome Slack, Microsoft Teams, Google Chat, and Webex all run on one backbone, and each new channel costs less than the one before it. Zero critical vulnerabilities maintained across these enterprise integrations for two years. ## What I'd do differently Define the shared intent schema on day one and version it like an API — merging divergent intent definitions later is the expensive path. Build the adapter last: get the shared envelope and the testing story right first.
Bridging async chatbot conversations into Amazon Connect IVR flows at Espressive
## Problem Espressive's virtual support agent was built for asynchronous chat, where a two-second reply feels snappy. A phone call is a blocking, real-time channel — callers hear every hop in the pipeline as silence. The platform needed a voice channel without rewriting the existing chatbot backend. ## My role Architected and led the integration end to end: intent routing, speech-to-text/text-to-speech orchestration across Amazon Connect, Amazon Lex, AWS Lambda, and Polly, and the handoff into existing fulfillment. ## Constraints - Voice latency budgets are unforgiving — callers notice sub-second gaps that chat users never see. - The asynchronous fulfillment backend was production infrastructure shared with every chat channel; it could not be forked for voice. - Enterprise security requirements — authentication, secrets handling — applied to every new hop. ## Architecture Amazon Connect captures audio and streams to Lex for intent resolution; a Lambda bridge translates between the synchronous IVR flow and the asynchronous chatbot backend, then Polly synthesizes responses back into the call. Downstream lookups start speculatively on utterance-start instead of after intent resolution, and a caller's journey is correlated across Connect, Lex, Lambda, and downstream APIs so traces survive the channel boundary. ## Outcome Shipped as a production channel of the virtual support agent platform that helped reduce help-desk call volume by 40–60%. ## What I'd do differently Design cross-service correlation in from day one rather than adding it during hardening — traces that die at a channel boundary are not traces. And budget for barge-in cancellation semantics early: every in-flight synthesis becomes cancellable, which changes idempotency requirements downstream.