I Built a CLI That Tells You Which Free Perks Your Open-Source Project Qualifies For

Published: (March 15, 2026 at 08:33 PM EDT)
7 min read
Source: Dev.to

Source: Dev.to

TL;DR: Open‑source maintainers leave thousands of dollars in free credits and tools on the table because nobody aggregates them. I built OSS Perks – a website + CLI that lists 15+ vendor perk programs and checks your repo’s eligibility with one command.

The problem

Vercel, Sentry, JetBrains, Cloudflare — they all give free stuff to OSS maintainers:

  • $3,600 in hosting credits
  • 5 million error events
  • Unlimited IDE licenses

Nobody knows about half of them.

All the offers are scattered across different websites, buried in marketing pages, each with its own eligibility rules and application steps. I spent a weekend Googling “free tools for open source” and jumping between tabs trying to figure out what my projects qualified for.

Then I saw getfirstcheck.com by @5harath – a curated directory of startup programs offering founders free credits and grants. After seeing the launch tweet, it clicked: founders had firstcheck, but OSS maintainers had nothing like it.

So I built OSS Perks.

What it is

Two things:

  1. A website – searchable directory of OSS perk programs, available in 9 languages.
  2. A CLI – run ossperks check in any repo and it tells you what you qualify for.

OSS Perks overview

Architecture

ossperks/
├── packages/
│   ├── data/          # JSON programs + Zod schemas
│   └── cli/           # CLI that checks eligibility
├── docs/              # Next.js 16 + Fumadocs website
└── pnpm-workspace.yaml

The main idea: @ossperks/data is the single source of truth. Every program is a JSON file, e.g.:

{
  "slug": "vercel",
  "name": "Vercel for Open Source",
  "provider": "Vercel",
  "url": "https://vercel.com/open-source-program",
  "category": "hosting",
  "description": "Vercel provides platform credits, community support, and an OSS Starter Pack.",
  "perks": [
    {
      "title": "$3,600 Platform Credits",
      "description": "$3,600 in Vercel platform credits distributed over 12 months."
    },
    {
      "title": "OSS Starter Pack",
      "description": "Credits from third‑party services to boost your project."
    }
  ],
  "eligibility": [
    "Must be an open-source project that is actively developed and maintained.",
    "Must show measurable impact or growth potential.",
    "Must follow a Code of Conduct.",
    "Credits must be used exclusively for open-source work."
  ],
  "duration": "12 months",
  "tags": ["hosting", "deployment", "serverless", "credits"]
}

This single file feeds three things:

  • Website – renders a program page.
  • CLI – uses it for list, show, search.
  • Build script – converts it to MDX, then lingo.dev translates it into 8 additional languages.

Change the JSON, everything updates.

The core trick: eligibility checking

ossperks check reads your repo metadata from GitHub/GitLab and runs every eligibility rule through a chain of matchers.

const matchRule = (rule: string, ctx: RepoContext): RuleVerdict =>
  checkSubjective(rule, ctx) ??
  checkProvider(rule, ctx) ??
  checkStars(rule, ctx) ??
  checkActivity(rule, ctx) ??
  checkLicense(rule, ctx) ??
  checkRepoAttrs(rule, ctx) ?? { reason: rule, verdict: "unknown" };

Each checker uses regex on the human‑readable eligibility string to infer the rule type, then validates against the repo. Example – the license checker:

const checkLicense = (rule: string, ctx: RepoContext): RuleVerdict | null => {
  const label = ctx.license ?? "no detected license";

  if (/permissive\s+(?:open[\s-]?source\s+)?licen[sc]e/i.test(rule)) {
    return isPermissive(ctx.license)
      ? { verdict: "pass" }
      : {
          reason: `requires a permissive license (detected: ${label})`,
          verdict: "fail",
        };
  }

  if (/open[\s-]?source\s+licen[sc]e|recognized\s+licen[sc]e/i.test(rule)) {
    return isOsiApproved(ctx.license)
      ? { verdict: "pass" }
      : {
          reason: `requires an OSI‑approved license (detected: ${label})`,
          verdict: "fail",
        };
  }

  return null;
};

No program‑specific if statements are needed – the eligibility rules live as strings in JSON, and the engine pattern‑matches the intent and checks the repo. Adding a new program is simply a matter of dropping a new JSON file; the checker handles it automatically.

Sample output:

✔ next.js — MIT · 131,247 stars · last push today

Eligibility across 15 programs — 8 eligible, 5 need review, 2 ineligible

  ✔ vercel            eligible
  ✔ sentry            eligible
  ✔ github-copilot    eligible
  ✔ jetbrains         eligible
  ⚠ cloudflare        needs review
     • non‑commercial requirement cannot be auto‑verified
  ✖ browserstack      ineligible
     • requires 500+ stars (you have 42)

Data pipeline: JSON → 9 languages

packages/data/src/programs/*.json     (source of truth)


docs/scripts/generate-programs-mdx   (convert JSON → MDX)


lingo.dev                           (translate MDX into 8 additional languages)

The pipeline ensures that every program page is available in all supported languages with a single source of truth.

.mjs  (JSON → MDX)


docs/content/programs/en/*.mdx        (English)

          ▼  lingo.dev
docs/content/programs/{es,fr,de,ja,ko,zh-CN,pt-BR,ru}/*.mdx

Generation script

The generation script builds structured Markdown from each JSON program:

const buildMarkdownBody = (p) => {
  const sections = [
    buildMetaSection(p),
    buildPerksSection(p),
    buildEligibilitySection(p),
    buildRequirementsSection(p),
    buildApplicationProcessSection(p),
    buildTagsSection(p),
  ].filter(Boolean);
  return sections.join("\n\n");
};

Parsing on the website

On the website, the translated MDX gets parsed back into structured data for rendering:

const parsePerks = (
  section: string
): { title: string; description: string }[] =>
  section
    .split("\n")
    .filter((l) => /^-\s+\*\*/.test(l.trim()))
    .map((line) => {
      const match = line.match(/\*\*(.+?)\*\*\s*[::]\s*(.*)/);
      return match
        ? { description: match[2], title: match[1] }
        : { description: "", title: line };
    });

Note: JSON → MDX → parse back to structured data is a round‑trip. The pragmatic decision was to let lingo.dev translate the MDX files rather than the raw JSON.

Why this stack

Approach

StackReason
Static site + JSON APINo i18n, no docs framework
AstroLess i18n ecosystem at scale
DocusaurusHeavier, React 18 only at the time
Fumadocs + Next.js 16 (chosen)i18n for free, MDX, OG images, search. Provides locale‑prefixed routes, language switching, OG image generation, and per‑locale content sources out of the box.
CommanderStandard CLI library

Validation with Zod

Every program goes through Zod at import time. Bad JSON fails immediately:

export const programSchema = z.object({
  applicationProcess: z.array(z.string()).optional(),
  applicationUrl: z.string().url().optional(),
  category: categoryEnum,
  contact: contactSchema.optional(),
  description: z.string(),
  duration: z.string().optional(),
  eligibility: z.array(z.string()),
  name: z.string(),
  perks: z.array(perkSchema),
  provider: z.string(),
  requirements: z.array(z.string()).optional(),
  slug: z.string(),
  tags: z.array(z.string()).optional(),
  url: z.string().url(),
});

export const programs: Program[] = raw.map((p) => programSchema.parse(p));

Trade‑offs

  • MDX round‑trip – JSON → MDX → parse back is awkward, but necessary because lingo.dev translates MDX, not JSON.
  • Regex eligibility matching – Works for the current 15 programs but is brittle; structured rules would be more robust long‑term.
  • No auth by default – The CLI hits the GitHub API unauthenticated, so you may hit rate limits. Set GITHUB_TOKEN to avoid this.
  • Limited program count – Only 15 programs are included; dozens more (DigitalOcean, AWS, MongoDB, Datadog, etc.) just need their JSON files added.

Try it

Website

git clone https://github.com/Aniket-508/ossperks.git
cd ossperks
pnpm install
pnpm --filter docs dev

CLI

npx @ossperks/cli

# Check the current repo
ossperks check

# Check a specific repo
ossperks check --repo vercel/next.js

# List all programs
ossperks list

# Search
ossperks search hosting

# Show program details
ossperks show vercel

What to build next

If you fork this repository:

  • Add programs – Create a {slug}.json and submit a PR. That’s it.
  • Structured eligibility rules – Replace free‑text with objects like { "type": "min-stars", "value": 500 } so the checker doesn’t rely on regex.
  • GitHub Action – Run ossperks check in CI and post results as a PR comment.
  • Expiry tracking – Many perks expire after 12 months; reminders would be helpful.
  • Community submissions – The API route (/api/submit-program) already exists.

If you maintain an open‑source project, run npx @ossperks/cli check. You might be surprised what you qualify for.

  • GitHub:
  • Website:
  • NPM:

Know a missing program? Open an issue or add a JSON file. It takes about 5 minutes.

0 views
Back to Blog

Related posts

Read more »

Travigo

Travel as fast as you speak with Gemini! Where live agents meet immersive storytelling & 3D navigation. This project was created for entering the Gemini Live Ag...

Micro games

Hey Gamers! 👾 As part of the Rapid Games Prototyping module, we are tasked with reviewing a peer's game. The challenge is to analyse a prototype built in just...