Skip to content

Repository files navigation

PulseOps

SaaS revenue metrics with an AI analyst — a lightweight alternative to ChartMogul for indie founders.

Point your billing webhooks at PulseOps and it turns a raw stream of signups, payments and churn into live MRR, ARR, churn and conversion — plus a one-click AI analyst that reads the numbers and tells you what's moving and what to do next. In realtime.

Live demo: https://pulse-ops-ai-five.vercel.app/ Demo login: demo@pulseops.app · Passkey123! (or sign in with Google / passkey)

The demo runs on a sample workspace with generated data so you can explore without connecting a real Stripe account.


The problem

A solo founder or a small SaaS team (1–5 people) on Stripe has revenue coming in — but no clear read on it. Stripe's own dashboard shows transactions, not MRR, churn or conversion, and it never tells you what to do. The dedicated tools that do — ChartMogul, Baremetrics, ProfitWell — start at $100–500/mo, which is hard to justify pre–Series A. So founders end up back in spreadsheets, guessing whether growth is real and whether churn is creeping.

The product

PulseOps ingests billing events (Stripe-style webhooks) through a simple API and derives the metrics that matter, in realtime:

  • Live metrics — MRR, ARR, active users, churn and conversion, computed from the event stream (not stored and drifting — derived, so they're always consistent and auditable).
  • Realtime event feed — new events appear the moment they land, over Supabase Realtime.
  • AI analyst — the differentiator. ChartMogul shows you charts; PulseOps also explains them. One click produces a headline, highlights, anomalies and concrete next steps, grounded in your live numbers and a statistical anomaly pass (see below) — the model explains real signals instead of inventing them.
  • Analytics — MRR-over-time, signups per day, event-mix, an MRR movement waterfall (new/expansion/reactivation vs. contraction/churn), weekly cohort retention, and statistically detected anomalies.
  • Passwordless auth — passkeys (Face ID / Touch ID / Windows Hello), Google OAuth and hCaptcha, all via Supabase.

About the demo

The public demo uses a sample workspace — click Load sample data to generate ~60 days of realistic history, or Simulate live events to watch the realtime feed update. Every workspace can also switch to Live mode and connect a real Stripe account (see below) — the same analytics engine, same AI analyst, running on real billing events instead of generated ones.

Connect your Stripe account (optional)

PulseOps can ingest real Stripe billing events instead of sample data. This is a genuine webhook integration — signature-verified, per-workspace — not a mock.

  1. Go to Settings in the app. Copy the webhook URL shown there (unique per account).
  2. In the Stripe Dashboard (Test mode is fine — no real money needed): Developers → Webhooks → Add endpoint, paste the URL, and subscribe to these events: customer.created, customer.subscription.created, customer.subscription.updated, customer.subscription.deleted, invoice.payment_succeeded, invoice.payment_failed.
  3. Stripe shows a signing secret (whsec_...) after the endpoint is created — paste it back into the Settings page and save.
  4. Switch the toggle to Live. Trigger a test event from Stripe (or use their test clocks) and watch it land in the event feed in real time.

Under the hood: each Stripe event is verified (HMAC-SHA256 over the raw body, timing-safe comparison, replay-window check — the same algorithm Stripe's own SDK uses) and mapped to PulseOps' internal event model — subscription prices are normalized to a monthly amount regardless of billing interval, and Stripe's opaque customer IDs are deterministically hashed into the UUIDs the schema expects. All of that mapping logic is pure and covered by unit tests.

My role

Solo — product, design and full-stack engineering. I scoped the product, designed the brand and UI, modelled the data, built the ingestion pipeline, the analytics engine, the AI layer and the auth, and shipped it to production on Vercel.

Key technical decisions

Each choice was deliberate — the reasoning matters more than the stack itself:

  • Event-sourced metrics. Events are the source of truth; metrics are derived by a pure function, never stored. This keeps numbers self-consistent and auditable, and makes the whole domain trivial to test.
  • Drizzle over TypeORM. Started on TypeORM, hit friction unfit for serverless (decorators, reflect-metadata, dynamic-require driver issues, a separate migration CLI). Swapped to Drizzle while the DB was still empty — lighter runtime, SQL-first migrations, no decorators.
  • Supabase for auth + realtime + Postgres. One managed source of truth for sessions (passkeys, OAuth, captcha) and live subscriptions — no second auth library, no websocket server to run.
  • Groq for the AI layer. OpenAI-compatible, generous free tier, ~2s inference. A strict JSON contract (schema in the prompt + response_format: json_object + defensive parsing + zod validation) keeps model output safe without paid structured-outputs.
  • Feature-Sliced Design. A strict, dependency-directed layering that keeps the codebase scalable and the pure domain isolated from framework glue.
  • Serverless-aware DB pooling. Capped pg pool per lambda so the Supabase pooler isn't exhausted under fan-out.
  • No Stripe SDK for the webhook path. Signature verification is ~20 lines of crypto (HMAC-SHA256 + timing-safe compare) — pulling in the full SDK for that alone isn't worth the weight. The mapping and verification logic is pure and unit-tested.

Architecture

src/
├─ app/        Next.js App Router (routing, layouts, route handlers)
├─ views/      Composed page bodies (FSD "pages" layer)
├─ widgets/    Self-contained UI blocks (sidebar, topbar, charts, event feed)
├─ features/   User-facing capabilities (ai-analyst, auth, realtime, stripe-integration)
├─ entities/   Business domains (event, metric, user) + pure domain logic
└─ shared/     Framework-agnostic primitives (ui, lib, api clients, config)

The domain layer is pure and framework-free — analytics, time-series, the event generator, and formatters take plain data and return plain data. That's what makes it fast to test and reason about.

Tech stack

Area Choice
Framework Next.js 16 (App Router, Turbopack), React 19
Language TypeScript (strict)
Styling Tailwind CSS v4, shadcn/ui components on @base-ui/react
Charts Recharts
Database Postgres via Supabase, Drizzle ORM
Auth Supabase Auth — passkeys, Google OAuth, hCaptcha
Realtime Supabase Realtime
Validation zod
AI Groq (OpenAI-compatible, free tier)
Testing Vitest (+ v8 coverage)
Architecture Feature-Sliced Design

Testing

The pure domain layer is covered to 100% (statements / branches / functions / lines) with Vitest. Framework glue (Server Components, route handlers, Supabase, Drizzle) is intentionally excluded — testing it would mostly test mocks, not logic.

npm run test           # watch mode
npm run test:run       # single run
npm run test:coverage  # with coverage report

Covered: computeAnalytics, computeDailySeries / computeEventTypeCounts, computeMrrMovement, computeCohortRetention, detectAnomalies, the event generator, currency/percent/relative-time formatters, the AI response schema + JSON extraction, and the Stripe integration (customer-ID hashing, monthly-amount normalization, event mapping, signature verification).

Getting started

Prerequisites

  • Node.js 20+
  • A Supabase project (free tier)
  • A Groq API key (free, optional — the app runs without AI)

Setup

npm install
cp .env.example .env.local     # then fill in the values (see below)
npm run db:generate
npm run db:migrate
npm run dev

Open http://localhost:3000, sign up, then click Load sample data to populate the workspace.

Environment variables

Variable Required Purpose
NEXT_PUBLIC_SUPABASE_URL Supabase project URL
NEXT_PUBLIC_SUPABASE_ANON_KEY Supabase publishable/anon key
SUPABASE_SECRET_KEY Supabase secret key (server-only)
DATABASE_URL Postgres connection string
NEXT_PUBLIC_SITE_URL Base URL for OAuth/passkey redirects
NEXT_PUBLIC_HCAPTCHA_SITE_KEY hCaptcha site key (auth works without it)
GROQ_API_KEY Enables the AI Analyst
GROQ_MODEL Override the default model

Scripts

Script What it does
npm run dev Start the dev server (Turbopack)
npm run build Production build
npm run start Serve the production build
npm run lint Lint with ESLint
npm run test / test:run / test:coverage Vitest
npm run db:generate / db:migrate Drizzle schema & migrations

A self-initiated product MVP. Data in the live demo is synthetic; not affiliated with Stripe, ChartMogul or any named company.

About

Enterprise-grade SaaS control tower built with Next.js, TypeScript, Supabase, and AI. Features real-time events, business analytics, KPI monitoring, intelligent insights, and a simulated multi-tenant SaaS backend inspired by Stripe and modern product operations platforms.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages