Project Overview
Wall-E is an open-source RAG-powered chatbot platform that lets any website owner embed a custom AI assistant trained on their own content — docs, PDFs, URLs, or plain text — without writing a single line of backend code.
Built as a solo indie project under hellocoders.in, it is a full-stack SaaS with a public dashboard, a FastAPI backend handling ingestion and retrieval, and a tiny vanilla JS embed widget that drops onto any site with a one-line script tag.
Timeline: Jan 2025 → Ongoing Role: Solo Full-Stack + GenAI Engineer (open-source)
The Problem
Most chatbot-builder tools in the market are either locked behind expensive SaaS paywalls, lack real RAG (they just prompt-stuff context), or produce bulky embeds that break site styling.
The specific gaps Wall-E targets:
- No open-source alternative with a usable hosted tier
- Existing tools offer no partial-source ingestion (upload a folder, skip certain files)
- Widget customization is usually theme-only — no behavioral config
- Payment flows for Indian founders are an afterthought (USD-only, no Razorpay)
- Deployment is typically a black box — no self-host path
Architecture & Tech Stack
- Frontend: Next.js 14 (App Router) + Tailwind CSS, hosted on Vercel
- Auth: Clerk (multi-tenant user management, org isolation)
- Backend: FastAPI (Python) — ingestion, retrieval, chat, webhook handling
- Vector DB: ChromaDB (per-chatbot collection isolation)
- LLM: Gemini 2.5 Flash via LangChain
- Payments: Razorpay (subscriptions + webhooks, migrated from Next.js API routes to FastAPI)
- Embed: Vanilla JS widget — four modular source files (styles, HTML, animations, core)
- Infra: Docker Compose, shared PostgreSQL, GitHub Actions CI/CD
Data Model
Chatbot
├── id, userId, name, settings, widgetConfig
├── DataSource[]
│ ├── id, type (pdf | url | text), status, chunkCount
│ └── ingestionError?
├── ChatSession[]
│ ├── id, visitorId, createdAt
│ └── Message[]
│ ├── role, content, retrievedChunks[]
│ └── latencyMs
└── Subscription
├── razorpaySubscriptionId, plan, status
└── currentPeriodEndRAG Pipeline Design
The ingestion and retrieval pipeline was the core engineering challenge.
Ingestion Flow
- User uploads a source (PDF, URL, or raw text) via the dashboard
- FastAPI queues a background ingestion job
- Source is chunked with LangChain's
RecursiveCharacterTextSplitter(chunk_size=800, overlap=100) - Each chunk is embedded via Gemini embeddings and stored in a per-chatbot ChromaDB collection
- Ingestion status tracked in PostgreSQL (
pending → processing → done / failed)
Retrieval Flow (per user message)
- Query is embedded and top-k similar chunks retrieved from ChromaDB
- Retrieved chunks are ranked and injected into a structured prompt
- Gemini 2.5 Flash generates a response grounded in retrieved context
- Source citations optionally surfaced to the end user
Key Design Decisions - Per-chatbot ChromaDB collections (not a shared collection with metadata filters) for clean tenant isolation and faster retrieval - Async ingestion — dashboard shows live status without blocking the UI - Idempotent re-ingestion — re-uploading the same source deletes old chunks before re-embedding
Embeddable Widget System
The widget is a self-contained vanilla JS bundle — no React, no build step for the site owner. It injects a chat bubble + panel into any page with:
<script src="https://wall-e.hellocoders.in/widget.js" data-chatbot-id="abc123"></script>Architecture After Refactor (four modular source files) - widget-styles.js — injects scoped CSS into shadow DOM, preventing host-site style collisions - widget-html.js — declarative HTML template for the chat panel and trigger button - widget-animations.js — open/close transitions, typing indicator, message entrance - widget-core.js — event listeners, API calls, session management, message rendering
Notable Bugs Resolved - Double-listener click bug — trigger button was accumulating event listeners across re-renders; fixed by tracking listener attachment state and removing before re-adding - z-index wars — shadow DOM encapsulation was the correct fix, not z-index escalation - Mobile keyboard pushing layout — viewport meta detection + dynamic height recalculation
Payments & Subscription Flow
Razorpay subscription integration was migrated from Next.js API routes to FastAPI to consolidate all payment logic server-side.
Subscription Flow
- User selects a plan on the dashboard
- FastAPI creates a Razorpay subscription and returns a checkout token
- Razorpay Checkout SDK handles payment on the frontend
- Webhook (
POST /payments/webhook) validates signature, activates plan, updates PostgreSQL - Failed payments trigger grace period logic (3-day window before chatbot deactivation)
Key Integration Details
- Webhook signature validation using Razorpay HMAC — no raw body parsing issues after switching to FastAPI's Request.body()
- Idempotency on webhook handler — duplicate events from Razorpay are deduplicated by event ID
- Subscription status synced to chatbot active/inactive state — unpaid chatbots return a soft error to the widget
Open-Source Monorepo Setup
Wall-E is fully open-source with a production-grade repo structure:
READMEwith architecture diagram, quickstart, and self-host guideCONTRIBUTING.mdwith PR guidelines and issue templatesLICENSE(MIT)- Docker Compose — spins up FastAPI + Next.js + PostgreSQL + ChromaDB with one command
- GitHub Actions CI — lint, type-check, and test on every PR
- Environment variable schema documented with
.env.example
The goal was to make self-hosting achievable in under 30 minutes for any developer familiar with Docker.
Key Learnings
- Shadow DOM is the right abstraction for embeddable widgets — fighting z-index and CSS specificity without it is a losing battle
- Per-tenant vector collections beat metadata-filtered shared collections for both isolation guarantees and query latency
- Migrate payment webhooks to your primary backend early — Next.js API routes are too fragile for webhook reliability (cold starts, body parsing edge cases)
- Modular widget source files pay off immediately — debugging a monolithic 800-line widget.js is miserable
- Open-source scaffolding should be set up at project start, not retrofitted — it shapes how you write code from day one
