Build log · LeadCore AI
How LeadCore AI is actually built
LeadCore AI turns anonymous website visitors into scored, enriched leads through an embeddable AI chat widget. This is the engineering reasoning behind it, taken from the system documentation of the running product — not a pitch.
01 · WidgetEmbed on the customer's siteA short script tag; no customer credentials ever live in the page.
02 · SignHMAC-SHA256 session tokenTokens are minted server-side and compared with timingSafeEqual, so a forged token fails identically to a wrong one.
03 · QualifyEdge worker runs the conversationCloudflare Workers keep first response fast worldwide; the scoring rule lives server-side, not in the widget.
04 · CapHard limits enforced in database RPCsUsage caps are checked inside Postgres functions, so no application code path can bypass them.
05 · Notifypgmq queue to templated email13 templates dispatched off a durable queue, so a provider outage delays alerts instead of losing them.
The queue is the reason a slow email provider never blocks a visitor's conversation: qualification completes, the notification drains later.
- What the product does: A multi-tenant SaaS that replaces a static contact form with a conversation. The assistant extracts name, email, phone, company, budget and project scope from natural language, computes an intent score out of 100, marks the lead hot, warm or cold, and fires a hot-lead alert email at score 60 or above. The row appears in the owner's dashboard within about a second.
- Architecture: one edge app, no servers to babysit: A single TanStack Start v1 application (React 19, Vite 7, SSR) deployed to Cloudflare Workers, backed by managed Supabase Postgres 15. There are no long-lived servers — every request runs in an ephemeral Worker isolate. All privileged logic lives in createServerFn functions or file-based server routes; the browser only ever receives publishable keys.
- Request lifecycle: Cloudflare edge terminates TLS/HTTP3, the Worker matches the route and runs its beforeLoad guard, functionMiddleware attaches the Supabase bearer token, Postgres RLS validates auth.uid() against the row owner, any AI call goes out server-side with the gateway key, and the response streams back with Sentry capturing errors on both sides.
- The runtime constraint that shapes every dependency choice: Because the bundle targets workerd with nodejs_compat, Node-native modules such as child_process, sharp and puppeteer simply cannot ship. Anything heavy is delegated to browser APIs or a third-party service. This is the single most common reason an otherwise fine npm package gets rejected during a build like this, and it is worth knowing before you pick a stack rather than after.
- Widget security: the part people usually get wrong: The widget is two pieces: a lightweight /embed.js loader the customer pastes on their site, and an iframed React app that talks to the chat API. Tokens are HMAC-SHA256 signed, short-lived and bound to the calling origin, compared with timingSafeEqual so signature checks are constant-time. If a widget key leaks, the origin allow-list limits the blast radius and a rotate_widget_key RPC replaces it with an owner notification. An embed snippet with a raw API key in it is a public API key — that design was never on the table.
- AI pipeline and cost: All model calls route through a managed gateway keyed server-side, defaulting to gemini-2.0-flash for its cost profile, throughput and large context window, which matters because scoring replays the whole conversation. Gateway 429s surface as a friendly retry; a 402 surfaces to the admin as credits exhausted rather than failing silently. Modelled spend at 500 messages per user per month across roughly 800 users comes out near a dollar — the expensive part of this product was never the tokens.
- Limits instead of billing: There is deliberately no payment gateway. Each workspace carries a plan string, plan_limits stores the numeric ceilings, and server-side RPCs — consume_ai_credit, check_leads_cap, enforce_workspace_domain_cap — enforce them atomically in the database rather than in application code where a race can slip through. Upgrades are a request that a super-admin approves, which was the right call for a product without paying volume yet.
- Tenant isolation and roles: 29 tables, RLS on the user-facing ones, and roles stored in a dedicated user_roles table checked through a SECURITY DEFINER has_role() function. Roles are never read from a profile row, because a role column on a profile is a privilege-escalation bug waiting for someone to find it.
- Email: a queue, not a fire-and-forget call: 13 React Email templates render server-side. Dispatch enqueues into pgmq, a trigger schedules pg_cron, the cron handler drains the batch every few seconds and calls the mail API, email_send_log records the result, suppressed_emails honours unsubscribes and bounces, and the cron unschedules itself when the queue empties. Sending mail inline from a request handler is how alerts get silently lost when the provider has a bad minute.
- Observability and admin plane: Sentry on browser and server, plus admin_audit_logs recording every super-admin action with actor, reason and before/after state. The admin app can rotate widget keys, tune rate-limit configs live, block IPs and review suspensions — and god-mode accounts are un-suspendable by automation so an automated rule cannot lock out the operator.
- Failure modes that were designed for, not discovered: AI gateway 429/5xx returns a retry banner rather than a dead spinner. A leaked widget key is detected from suspicious domains in embed logs and rotated by RPC. A Sentry blackout falls back to console plus Cloudflare logs. The HMAC signing secret rotates through system_secrets with a dual-key grace window, so rotation is not an outage.
- Honest state of the product: LeadCore AI is in public beta and it is my own product, not a client engagement. Real-time voice is on the roadmap and sold as roadmap only. Payments are not built. The security posture was scored in an ethical pentest in July 2026 rather than by me marking my own homework, and I will show you the report rather than quote a number at you.
- What I would do differently: Put the hard caps in database RPCs from the first migration instead of the third, because moving enforcement out of application code later means auditing every write path. Ship the email queue before the first alert feature rather than after. And instrument lead-score accuracy from day one — the scoring rule is the product, and without a labelled sample you are tuning it on vibes.
- See it running: LeadCore AI is live at leadcore.agenticcore.tech. Install the embed on a test page and try to break the qualification flow — that is a more useful reference than anything I could write here.
Questions about any of this? Email daniyal@agenticcore.tech — I reply within one business day.