I Built a CLI That Tells You Which Free Perks Your Open-Source Project Qualifies For
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:
- A website – searchable directory of OSS perk programs, available in 9 languages.
- A CLI – run
ossperks checkin any repo and it tells you what you qualify for.
Architecture
ossperks/
├── packages/
│ ├── data/ # JSON programs + Zod schemas
│ └── cli/ # CLI that checks eligibility
├── docs/ # Next.js 16 + Fumadocs website
└── pnpm-workspace.yamlThe 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}/*.mdxGeneration 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
| Stack | Reason |
|---|---|
| Static site + JSON API | No i18n, no docs framework |
| Astro | Less i18n ecosystem at scale |
| Docusaurus | Heavier, 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. |
| Commander | Standard 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_TOKENto 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 devCLI
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 vercelWhat to build next
If you fork this repository:
- Add programs – Create a
{slug}.jsonand 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 checkin 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.
Links
- GitHub:
- Website:
- NPM:
Know a missing program? Open an issue or add a JSON file. It takes about 5 minutes.
