Live intelligence on companies moving through private-equity deal lifecycles — carveouts and private-asset exits. Tracks 1,000+ companies across stages (in market → monitor for exit → on hold → pulled), with an analyst feed, analytics, a review queue, and automated signal ingestion.
- Next.js 15 (App Router, TypeScript) · Tailwind · Recharts
- Supabase — Postgres, Auth (email/password), Realtime, RLS
- Anthropic Claude — structured signal extraction (forced tool call)
- Typesense — search + entity resolution (optional; Supabase
ilikefallback) - Upstash Redis — LLM rate-limit + pipeline lock (optional; no-op if unset)
- Vercel — hosting + cron
Every external dependency is env-guarded. With no .env.local, the app runs
fully on mock data: all pages render, API routes return 503, and client
fetchers fall back to mock. Add keys and each subsystem "flips on" — no code
changes. The mock frontend is the contract the backend was built against.
pnpm install
pnpm dev # http://localhost:3000 → redirects to /radarNo environment needed. Explore /radar, /feed, /analytics, /review, /watchlist, /company/[id]. /admin and auth require the live backend (below).
-
Create a Supabase project. Copy
.env.local.example→.env.localand fill the Supabase block (NEXT_PUBLIC_SUPABASE_URL,…_ANON_KEY,SUPABASE_SERVICE_ROLE_KEY) andANTHROPIC_API_KEY. -
Apply migrations (Supabase SQL editor, in numeric order): every file in
supabase/migrations/(0001_init.sql…0018_signal_dedupe.sql), thensupabase/seed.sql. Seesupabase/README.md. Migrations are additive + idempotent, so re-running is safe. -
Create users. Self-signup is enabled at
/signup(new users default to theanalystrole). Promote someone toadminfrom the Admin page, or setuser_metadata.role = "admin"in the Supabase dashboard for the first admin. -
Run.
pnpm dev. Unauthenticated requests are redirected to/login(API routes return401). Realtime stage changes stream into /radar + /feed.
- Search (Typesense): set
TYPESENSE_*, thenpnpm sync:searchto create + fill thecompaniescollection. Without it, search uses a Supabaseilikefallback. - Rate-limit / lock (Upstash): set
UPSTASH_REDIS_REST_*. No-op if unset. - Web signals (Google CSE): set
GOOGLE_CUSTOM_SEARCH_*for the private-asset pipeline. Without it, that pipeline runs on RSS only.
Two cron jobs (vercel.json) feed the tracker:
- Carveouts (
/api/ingest/carveouts, every 6h) — SEC EDGAR full-text search. - Private assets (
/api/ingest/private-assets, every 12h) — Google CSE per tracked company + PE/M&A RSS.
Each fetches source text → Claude extracts a structured signal → entity
resolution (Dice ≥ 0.85) matches or creates a company → stage update + history.
Routes are guarded by CRON_SECRET and a distributed lock. Trigger manually
from the Admin page (admin only).
/login— email + password/signup— self-serve registration (role: analyst)/forgot-password→ email link →/auth/reset/auth/callback— email-confirmation + recovery code exchange- Role management — admins change roles / create users from
/admin
| Route | Description |
|---|---|
/radar |
Kanban + table radar — filters, summary strip, sector cards, add company |
/feed |
Activity feed grouped by day + sidebar (live, watchlist, range stats) |
/analytics |
Metric cards + Recharts (velocity, splits, funnel, heatmap, sources) |
/watchlist |
Manage watched companies |
/company/[id] |
Profile — timeline, signals, analyst notes (edit/delete), peers |
/review |
Analyst review queue — Confirm / Override |
/admin |
Pipeline health, system stats, user + role management (admin only) |
pnpm dev # dev server
pnpm build # production build
pnpm typecheck # tsc --noEmit
pnpm lint # eslint (max-warnings 0)
pnpm format # prettier --write
pnpm test # vitest (unit + route)
pnpm test:coverage # vitest + coverage gate
pnpm test:e2e # Playwright (mock mode)
pnpm sync:search # push companies → Typesense
pnpm extract:test # smoke-test the LLM extractor- docs/ARCHITECTURE.md — system shape, request lifecycle, multi-tenancy, ingestion data flow, env-gated subsystems, observability.
- docs/API.md — public API guide (auth, pagination, rate limits,
errors). Machine-readable spec at
GET /api/v1/openapi(OpenAPI 3.1). - docs/RUNBOOK.md — incident response, secret rotation, deploy/rollback, on-call checklist.
- CONTRIBUTING.md — setup, scripts, quality gates, conventions.
- SECURITY.md — vulnerability disclosure + hardening summary.
Beyond the analyst app, the platform includes: org/team management with email invites, role + member admin, Stripe billing (plan-tiered API quotas), a scoped public API, GDPR data export + account deletion, and a freshness-SLA monitor.
- Adapters (
lib/adapters/) are the seam: pure functions mapping DB snake_case rows → frontend camelCase shapes. No Supabase imports. useLive(lib/use-live.ts) — live fetch + mock fallback + realtime refetch.requireBackend()— API routes return503until Supabase env is set.- Typed Supabase client via
Databasegeneric intypes/db.ts(row shapes aretype, notinterface, to satisfy supabase-js's index-signature constraint).