Under the hood

How FundMatch decides what you are eligible for

Government funding decisions are rule-based, so our engine is too. Below is the full decision matrix, the system architecture, and the security posture we hold because this product sits next to public money.

Decision matrix

SignalCaptured fromRuleEffect on verdict
Region / stateIntake: state of operationsCentral schemes match all states; state schemes require an exact state code matchHard filter — mismatch removes the scheme
SectorIntake: what you are buildingScheme sector list must contain your sector or the wildcard 'any'Hard filter + 25 pts on exact sector fit
StageIntake: idea → growthScheme stage list must include your stageBlocker if absent, 20 pts if present
Entity typeIntake: pvt ltd / LLP / partnership / proprietorshipMost grants need a registered company or LLPBlocker — surfaced as 'incorporate first'
Entity ageIncorporation yearAge must be ≤ scheme max_age_yearsBlocker when older than the cap
DPIIT recognitionIntake togglerequires_dpiit schemes need active DPIIT recognitionActionable gap — free to obtain, so shown as 'with prep'
Women-ledIntake togglewomen_led_only schemes require ≥51% women founder ownershipHard filter for reserved schemes, bonus 10 pts elsewhere
IncubationIntake toggleSeed-fund style schemes route applications via approved incubatorsActionable gap with incubator guidance
Funding askIntake: amount neededAsk compared against scheme min/max ticketSoft score — up to 20 pts for ticket fit
RevenueIntake: annual revenueRevenue caps on early-stage grantsBlocker on breach, otherwise neutral

System architecture

1 · Presentation (TanStack Start, SSR)

Public marketing, catalogue and scheme detail pages are server-rendered for SEO and shareability. Founder-only surfaces sit behind a client-gated authenticated layout.

  • •Public: /, /schemes, /schemes/$slug, /onboarding, /matches, /architecture
  • •Private: /dashboard, /applications/$id under the _authenticated layout
  • •Intake runs anonymously in local storage so founders see value before signing up

2 · Matching engine (pure functions)

Eligibility is deterministic, versionable and unit-testable. No LLM in the decision path, so every verdict is explainable line by line.

  • •Hard filters produce blockers; soft signals produce a 0–100 fit score
  • •Verdicts: eligible now · eligible with prep · blocked
  • •Runs identically on server (SSR) and client (instant re-score on edits)

3 · Server functions (typed RPC)

Every read or write that touches founder data goes through an authenticated server function; the browser never holds privileged credentials.

  • •Public reads use a publishable-key server client with narrow anon SELECT policies
  • •Private reads/writes use per-request auth middleware — the query runs as the signed-in user
  • •Service-role access is never imported into client-reachable module scope

4 · Data & storage (Postgres + private buckets)

Curated scheme catalogue is the only public dataset. Everything founder-specific is row-scoped.

  • •schemes: verified, machine-readable eligibility metadata with source + last_verified_at
  • •startup_profiles, applications, application_documents: one owner per row
  • •Documents live in a private bucket keyed by user id / application id

5 · Curation pipeline

Scheme data is human-verified from official government sources rather than scraped blindly, because a wrong eligibility rule costs a founder real money.

  • •Every scheme records source_url, apply_url and last_verified_at
  • •Changes ship as migrations, giving a full audit trail of rule history
  • •Stale entries are deactivated with is_active rather than deleted

Security & compliance

  • ✓Row Level Security on every founder table, scoped to auth.uid() — no cross-tenant reads are possible even with a leaked anon key.
  • ✓Explicit GRANTs per role: anon can read only active schemes, never applications or profiles.
  • ✓Roles live in a dedicated user_roles table read through a security-definer function, so privilege escalation via profile edits is impossible.
  • ✓Document storage is private; files are namespaced per user and per application and are never publicly addressable.
  • ✓Financial fields (amount requested, bank details) are written only through authenticated server functions with schema validation.
  • ✓Submitted applications lock their form and checklist, preserving an immutable record of what was filed and when.
  • ✓No secrets in the browser bundle: privileged keys are read inside server handlers at request time.

Scaling posture

Catalogue growth
Scheme rules are data, not code — adding 500 more state schemes needs zero engine changes.
Read scale
Public catalogue and detail pages are SSR + cacheable; matching runs client-side after first paint.
Write scale
Applications are append-mostly with per-row ownership, so partitioning by user id stays trivial.
Review workflow
A reviewer role already exists in the policy layer for a future state-agency console.
Integrations
Official portal filing stays an outbound link until an API exists; the internal record is already portal-shaped.

Request flow

Founder intake (browser, anonymous)
   |
   v
Local draft profile  ----->  Matching engine (pure TS rules)
   |                              |
   |                              v
   |                     Ranked verdicts: eligible / with prep / blocked
   v
Sign in  -->  Authenticated server function  -->  Postgres (RLS: auth.uid())
                     |                                  |
                     v                                  v
            Private document bucket             applications + documents
                     |
                     v
        Submit -> locked record + reference no -> official portal filing

FundMatch prepares and tracks applications. Final sanction decisions always rest with the respective central or state authority.