Skip to content

Repository files navigation

FundSignal

🏆 Winner · TUM.ai E-Lab Hackathon (Munich) · Project A × Yellow × TUM.ai

AI-powered investor relationship graph for founders - find warm intro paths, not cold email lists.

FundSignal maps your existing founder network, detects investors within it, scores fit against your startup profile, and surfaces the shortest warm introduction path to each investor. Built as a hackathon MVP with a functional graph engine, BFS pathfinding, AI scoring, and a live Supabase backend mode.


🏆 Winner - Project A × Yellow × TUM.ai E-Lab Hackathon

FundSignal was built during the Project A × Yellow × TUM.ai E-Lab Hackathon in Munich, hosted by AI E-Lab by TUM.ai.

The event brought together builders for a 1-day startup hackathon focused on rapidly building, pitching, and stress-testing early-stage ideas.

The event was organized with Project A, Yellow, and TUM.ai E-Lab, with Lovable as sponsor.

Our team won the hackathon.


My Contribution

I drove the product and frontend direction for FundSignal, proposing the web-product architecture using Lovable and Supabase, designing the user experience and system flow, and building the website/frontend. I also contributed to the product identity, including the name and logo, and helped shape the AI/product workflow during the hackathon.

This was a team effort: the graph engine, backend, and AI implementation were built collaboratively with teammates. The description above reflects my role and does not imply sole ownership of the entire project.


What it does

Most founders approach fundraising backwards - they build a list of investors and then try to figure out how to reach them. FundSignal inverts this: it starts from your existing network, finds which investors are already reachable through people you know, and ranks them by the combination of fit and warmth.

The core loop:

  1. Ingest your network (LinkedIn Saved Search exports, CSV, CRM)
  2. Build a weighted relationship graph (founder → contacts → investors)
  3. Run BFS to find the shortest warm path to every investor
  4. Score each investor by sector fit, stage match, and warmth

System architecture

Data Sources
    │
    ├── LinkedIn Sales Navigator (Saved Search → weekly email alert)
    ├── Manual CSV upload (linkedin_saved_search_synthetic.csv)
    ├── Email contact API (simulated)
    └── CRM import (simulated)
    │
    ▼
Ingestion Layer
    │
    ├── HTML email parser (parses LinkedIn alert notification HTML)
    ├── CSV transformer (normalizes column schema → snake_case)
    └── Supabase upload (upsert via vc_id + synthetic_person_id unique key)
    │
    ▼
Supabase (PostgreSQL)
    │
    └── public.connection_list
        vc_name · vc_id · first_name · last_name · title · company
        location · linkedin_url · synthetic_person_id · degree · created_at
    │
    ▼
Analytics Engine (src/lib/supabaseAnalytics.ts)
    │
    ├── buildAnalytics() - aggregates rows into graph-ready data structures
    ├── Overlap detection - persons appearing under multiple vc_ids
    ├── VC ranking by people count and overlap connector count
    ├── Degree distribution, top companies, top locations
    └── Warm path construction (founder → person → vc)
    │
    ▼
Graph Engine (src/lib/graph.ts)
    │
    ├── findWarmPath() - BFS shortest path between any two nodes
    ├── computeWarmthScore() - edge strength × hop penalty
    ├── computeFitScore() - sector overlap, stage match, portfolio relevance
    ├── rankInvestors() - combined score = fit × 0.65 + warmth × 0.35
    └── generateIntroEmail() - personalized forwardable intro request
    │
    ▼
Frontend (React + TypeScript + Vite)
    │
    ├── NetworkGraph - SVG force layout with radial positioning
    ├── InvestorList - ranked cards with hover-to-highlight BFS path
    ├── SupabaseMode - live data dashboard with tabbed analytics
    └── JsonPreview - raw graph data export for downstream tooling

Core features

Graph engine

The graph is a weighted undirected network of nodes and edges. Node types: founder, contact, angel, vc, micro_fund, portfolio. Each edge carries a strength value (0–1) representing relationship quality - co-founder relationships score near 1.0, conference acquaintances around 0.6–0.7.

BFS pathfinding (findWarmPath) finds the shortest connection chain between the founder and any investor. Warmth score penalizes extra hops and rewards high-strength edges:

warmth = (100 − (hops − 1) × 15) × avgEdgeStrength

Investor fit score combines:

  • Sector overlap (up to 40 points)
  • Stage match (up to 25 points)
  • Portfolio relevance (up to 15 points)
  • Location match (up to 10 points)
  • Ticket size fit (up to 10 points)

Final combined score: fit × 0.65 + warmth × 0.35

Supabase integration

The connection_list table stores 2nd-degree LinkedIn connections for each VC in your network. The overlap detection query identifies people who appear in the connection lists of multiple VCs - these are the highest-signal founders to investigate for investment.

-- Overlap detection
SELECT
  synthetic_person_id,
  first_name,
  last_name,
  title,
  company,
  COUNT(DISTINCT vc_id)        AS vc_overlap_count,
  STRING_AGG(vc_name, ', ')    AS connected_vcs
FROM public.connection_list
GROUP BY synthetic_person_id, first_name, last_name, title, company
HAVING COUNT(DISTINCT vc_id) > 1
ORDER BY vc_overlap_count DESC;

The buildAnalytics() function (src/lib/supabaseAnalytics.ts) computes the full analytics object from raw rows in a single pass, producing VC nodes, person nodes, edges, overlap connectors, VC rankings, and warm paths - all without any additional database queries.

LinkedIn data pipeline

The intended production ingestion flow:

  1. Each VC in your network runs a LinkedIn Sales Navigator Saved Search filtered to their 2nd-degree connections
  2. LinkedIn sends a weekly alert email with new matches (5 preview cards per email)
  3. Your backend parses the HTML email (BeautifulSoup) to extract name, headline, company, LinkedIn URL
  4. Alternatively, the VC exports a full Lead List CSV from Sales Navigator (Advanced plan required)
  5. CSV is uploaded to Supabase via the frontend or a Python ingestion script
  6. Daily diff job compares new snapshot against previous, detects new connections, fires alerts on overlaps

For the hackathon demo, a synthetic dataset of 488 rows across 5 VCs with ~8% overlap is provided as linkedin_saved_search_synthetic.csv.


Tech stack

Layer Technology
Frontend framework React 18 + TypeScript
Build tool Vite 5 + SWC
Styling Tailwind CSS v3
Component library shadcn/ui (Radix UI primitives)
Data fetching TanStack React Query v5
Routing React Router v6
Database Supabase (PostgreSQL)
Supabase client @supabase/supabase-js v2
Charts Recharts
Icons Lucide React
Development platform Lovable
Package manager Bun / npm
Testing Vitest + Testing Library
Linting ESLint 9 + TypeScript ESLint

Project structure

fundsignal/
├── src/
│   ├── components/
│   │   ├── Architecture.tsx       # Pipeline flow diagram
│   │   ├── Hero.tsx               # Landing hero section
│   │   ├── IntroModal.tsx         # Intro email composer
│   │   ├── InvestorList.tsx       # Ranked investor cards
│   │   ├── JsonPreview.tsx        # Raw graph data export
│   │   ├── Metrics.tsx            # Live signal metrics
│   │   ├── NetworkGraph.tsx       # SVG relationship graph
│   │   ├── NodeDetailsPanel.tsx   # Node detail sidebar
│   │   ├── PipelineCards.tsx      # Data source status cards
│   │   ├── StartupCard.tsx        # Startup profile display
│   │   ├── TopNav.tsx             # Navigation bar
│   │   └── ui/                    # shadcn/ui primitives
│   │
│   ├── hooks/
│   │   └── useConnectionRows.ts   # Supabase live data hook
│   │
│   ├── integrations/
│   │   └── supabase/
│   │       ├── client.ts          # Supabase client singleton
│   │       └── types.ts           # Auto-generated DB types
│   │
│   ├── lib/
│   │   ├── data.ts                # Demo graph nodes + edges
│   │   ├── graph.ts               # BFS, scoring, email generation
│   │   ├── supabaseAnalytics.ts   # Analytics engine for live data
│   │   ├── types.ts               # GraphNode, GraphEdge, types
│   │   └── utils.ts               # cn() utility
│   │
│   └── pages/
│       ├── Index.tsx              # Demo mode main page
│       ├── NotFound.tsx           # 404 page
│       └── SupabaseMode.tsx       # Live Supabase dashboard
│
├── supabase/
│   └── config.toml                # Supabase project config
│
├── public/                        # Static assets
├── index.html                     # App entry point
├── package.json
├── tailwind.config.ts
├── vite.config.ts
└── vitest.config.ts

Supabase setup

1. Create the table

CREATE TABLE public.connection_list (
  id                   BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  vc_name              TEXT NOT NULL,
  vc_id                TEXT NOT NULL,
  first_name           TEXT,
  last_name            TEXT,
  title                TEXT,
  company              TEXT,
  location             TEXT,
  linkedin_url         TEXT,
  synthetic_person_id  TEXT NOT NULL,
  degree               INTEGER DEFAULT 2,
  created_at           TIMESTAMPTZ DEFAULT NOW(),
  CONSTRAINT unique_vc_person UNIQUE (vc_id, synthetic_person_id)
);

CREATE INDEX idx_vc_id     ON public.connection_list(vc_id);
CREATE INDEX idx_person_id ON public.connection_list(synthetic_person_id);
CREATE INDEX idx_created   ON public.connection_list(created_at);

2. Configure RLS

Enable RLS and add policies:

-- Enable RLS
ALTER TABLE public.connection_list ENABLE ROW LEVEL SECURITY;

-- Allow public read (tighten for production)
CREATE POLICY "Allow public read"
ON public.connection_list FOR SELECT USING (true);

-- Allow insert (use service_role key from backend)
CREATE POLICY "Allow insert"
ON public.connection_list FOR INSERT WITH CHECK (true);

3. Import the dataset

Go to Table Editor → connection_list → Insert → Import data from CSV and upload linkedin_saved_search_synthetic.csv. All 488 rows will be imported. The unique constraint on (vc_id, synthetic_person_id) prevents duplicates on re-import.

4. Environment variables

VITE_SUPABASE_URL=https://your-project-id.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=your-anon-key

The anon key is safe to expose in the frontend - it respects RLS policies. Use the service_role key only in server-side scripts (never commit it or expose it in the frontend).


Local development

# Install dependencies
npm install

# Start dev server
npm run dev
# → http://localhost:8080

# Run tests
npm test

# Build for production
npm run build

Deployment

The project is built on Lovable and deploys automatically on push. For manual deployment:

npm run build
# → dist/ folder ready for any static host

Compatible with Vercel, Netlify, Cloudflare Pages, or any static hosting. No server required - all logic runs client-side, with Supabase as the only external service.


Dataset

linkedin_saved_search_synthetic.csv - 488 rows of fully synthetic LinkedIn Sales Navigator 2nd-degree connection data:

  • 5 synthetic VC contacts (VC001–VC005)
  • 435 unique synthetic people
  • ~8% overlap (35 people appear in 2+ VC datasets)
  • Fields: vc_name, vc_id, first_name, last_name, title, company, location, linkedin_url, synthetic_person_id, degree
  • All names, companies, and URLs are fictional. No real personal data.
  • Ready for direct Supabase CSV import

Roadmap

The following are simulated in the demo and represent the production v2 build:

  • LinkedIn OAuth multi-user - each VC authorizes the app, connection diffs run automatically
  • Browser extension - passively captures connection data as users browse LinkedIn
  • Gmail API parser - ingests LinkedIn Saved Search weekly alert emails automatically
  • Zapier integration - Sales Navigator new match → webhook → Supabase upsert
  • Twitter/X signal layer - monitors VC engagement with founder accounts
  • Weekly diff alerts - email notification when a new person appears across 2+ VC networks
  • CRM export - push ranked investors and warm paths to HubSpot or Notion

License

Hackathon MVP - built for demonstration purposes. All demo data is synthetic.

About

Winner of the Project A × Yellow × TUM.ai E-Lab Hackathon — a platform for discovering warm investor-introduction paths and founder–investor fit.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages