DEV Community

Ibrahim Edhem Harbutlu
Ibrahim Edhem Harbutlu

Posted on

I built a self-hosted license key manager for my Gumroad products (0 sales so far)

I sell boilerplates on Gumroad. Source code, you download a ZIP, you run it. Over the last few months one thought kept nagging me: if I ever wanted to actually license one of these products, or sell a paid tool that checks a key when it runs, I would have to sign up for some license-key SaaS and pay monthly for the privilege. The cheap ones start around 20 a month. For a side project that makes nothing yet, that math is bad.
So I built the thing instead. It's called KeyMint, and it's a license key manager you host yourself.
Demo (with seeded data so it doesn't look like an empty shell): https://keymint.pages.dev
What it actually does
You create a product, generate a batch of keys, and hand them out. Then your own software checks a key with a single request:

const res = await fetch("https://your-keymint.app/api/validate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key, productSlug: "subsaver", activate: true }),
});
const { valid } = await res.json();

That valid boolean is the whole point for most integrations. The response also tells you why a key failed (revoked, expired, activation limit hit, wrong product) and how many activations are left, so you can do seat limits or device limits without writing that logic yourself. Pass activate: true and the check also burns one activation.
The dashboard handles the boring side: bulk key generation, revoke, expiry dates, and an activity log that records every validation attempt with its result and IP. I added a 30-day trend chart mostly because an empty dashboard is depressing and a populated one sells the idea in two seconds.
The stack, and the part that fought back
React 19 and Vite on the front, Express 5 and Drizzle on the back, Postgres underneath, all in a pnpm monorepo. Nothing exotic.
Deploying it as a broke person was the interesting part. The frontend is static, so it went on Cloudflare Pages for free. The API went on Railway. Then I hit Railway's free-tier limit when I tried to add a second service, and my Supabase free projects were already full from two earlier apps. So the database ended up on Neon, which gives you a real Postgres instance on a free tier that doesn't expire. Three providers, zero dollars a month. The schema and a seed script are in the repo, so spinning up your own copy is a SQL paste and two deploys.
One gotcha worth writing down: I bundle the API with esbuild, and pg has to be listed as a direct dependency of the server package or the deployed bundle can't find it at runtime. It built fine locally and crashed on first boot until I caught that. Exactly the kind of thing that wastes an hour.
Honest status
Zero sales. The product went live today. I have done this enough times now (this is my fourth boilerplate) to know that "published on Gumroad" and "someone bought it" are very far apart, and the gap is distribution, not the product. Writing this post is part of closing that gap, which I am aware is a little circular.
It's 49 dollars, MIT licensed, you get the full source. What it does not do, on purpose: it does not take payments (it manages keys, not money), and the admin dashboard has no auth by default because it's meant to run privately. The README shows the roughly ten lines to gate it if you want to expose it. No bank integrations, no hosted version, no monthly anything.
Link, if you want it: https://ibrh96.gumroad.com/l/nvkfg
The other ones
If you're the type who buys boilerplates, these are the other three I've shipped, same stack, same idea of skipping the setup grind:

SubSaver, a subscription and savings tracker: https://ibrh96.gumroad.com/l/gytqdv
FreelanceFlow, a client, project and invoice tracker: https://ibrh96.gumroad.com/l/bcsufq
MetricMint, an MRR dashboard for indie hackers: https://ibrh96.gumroad.com/l/jwdmao

A real question
Two things I keep going back and forth on, and I'd take actual opinions:

For a tool like this, does keeping the admin side auth-free by default make it feel unfinished, or is "private by default, gate it yourself" the right call for a starter kit?
If you sell digital products, how do you handle licensing today? Do you bother at all, or just trust people? I genuinely can't tell if I built something useful or solved a problem only I have.

Top comments (0)