The 2am Cancellation Problem: How GHRM Sells and Un-Sells a Private Repo Without You

A customer subscribes at midnight and cancels at 2am. Who grants and revokes their repo access while you sleep? VBWD’s GHRM does — deploy tokens, collaborator invites, and auto-revoke, all event-driven.

Rewritten for redrobot.

Here is a question that quietly ruins weekends: how do you sell access to code you keep in a private GitHub repo, and then take that access back the moment someone stops paying? Not the first sale. The first sale is easy. It is customer number eleven cancelling their card at 2am on a Sunday while you are asleep, and the awkward reality that they still have collaborator rights on Monday morning because nobody was awake to click “remove.”

Most people solve this with tape and string: a payment webhook, a shell script that shells out to the GitHub API, and a spreadsheet listing who is supposed to have access to what. It holds together right up until it doesn’t. GHRM is the piece of the VBWD platform built specifically so you never have to be the person clicking “remove.”

The trick is that the platform never learns what GitHub is

Before the mechanics, the design decision that makes all of this hang together. VBWD is a self-hosted, source-available platform: Flask and PostgreSQL underneath, Vue 3 on top, the whole thing shipped in Docker. Its core deliberately knows about a small, boring vocabulary: users, plans, subscriptions, invoices, and an event bus that broadcasts when any of those change. That is the entire world the core lives in.

What the core does not know, and is engineered to never know, is that GitHub exists. There is no GitHub code in it, no repo model, no collaborator concept. Anything domain-specific lives in a plugin, and plugins hook into the platform by listening for events rather than reaching into core code and rewiring it. If that split sounds like an academic nicety, it is not: it is the reason you can bolt GitHub distribution onto a billing system without the billing system ever becoming a GitHub tool. The architecture overview lays out why the core is kept this deliberately ignorant.

GHRM (“GitHub Repo Manager”) is one of those plugins. It sits at the edge, ears open, waiting for the core to announce that money has moved.

Follow one customer through the whole loop

The cleanest way to understand GHRM is to watch a single person move through it, because the entire system is a chain of reactions to events rather than a script that runs on a timer.

Someone lands on your storefront and buys a plan. The core does what the core does: it processes the subscription, and the instant it becomes active it fires a subscription.activated event onto the bus. It has no further opinion about what that means. GHRM, listening, catches the event and springs into action. It looks up which packages that plan grants, and a package is simply a pointer to one or more GitHub repositories plus the access they come with. For each repo, GHRM calls the GitHub API to invite the buyer as a collaborator on their own GitHub account, and issues any deploy tokens the package defines for machine and CI use. The customer accepts the invite, clones, wires the token into their pipeline, and is off.

Then nothing happens, which is the point. While the subscription stays active, access stays granted. Because access is bound to live subscription state rather than a one-time handout, there is no monthly ritual of cross-checking who actually paid against who still has keys.

Eventually they cancel, or a renewal lapses. The core emits the matching cancel or expiry event. Here GHRM does something worth noticing: it does not slam the door. It starts a configurable grace window, and only when that window elapses does it pull the collaborator invite and invalidate the deploy tokens. That delay is intentional. It absorbs the failed-then-retried payment, the card that expired and got updated a day later, the customer who cancelled by accident. A hard cutoff mid-workday makes enemies; a short runway does not.

Through all of this the core did exactly two things: it moved money and it changed a subscription’s state. It never found out a repository was involved. The GitHub-specific reactions are welded on at the outer edge, on top of the same subscription lifecycle events every other VBWD plugin already consumes. The documentation shows the shape of how a plugin subscribes to those events if you want to see the seams.

The other half nobody mentions: keeping the storefront honest

Granting and revoking access is only one job. The quieter job is making sure the product page is not lying about what buyers get. GHRM handles this by syncing repository metadata into the platform through GitHub Actions. When you cut a release, an Action pushes your README, CHANGELOG, and release notes into VBWD, and they surface on a catalogue page rendered by the CMS.

The effect is that your product listing stops being a marketing blurb you last touched three releases ago. The README becomes the description. The CHANGELOG shows what actually changed. Buyers see the current version’s reality without you copy-pasting notes between two systems and inevitably forgetting one. And because that catalogue page is an ordinary CMS page, you style it, link it into your navigation, and drop a buy button on it like any other content; the sync fills in the product-specific parts, the CMS owns how it looks.

What it honestly will not do

GHRM is a thin, well-mannered layer over GitHub, which means it inherits GitHub’s rules, and you should build on it with your eyes open.

The largest caveat first: it gates ongoing access, not copies already made. The moment someone clones your repository, that clone lives on their disk permanently. Revoking their collaborator status stops them pulling future commits and releases and kills their deploy token, but it does not reach across the internet and delete what they already downloaded. What you are actually selling is continued updates and legitimate, current access, not a copy-protection scheme. Price it and describe it as exactly that.

Revocation is also delayed by that grace window, by design. If your business genuinely needs a hard cutoff the second a payment fails, this is not built for that, and pretending otherwise would just annoy honest customers caught by a transient billing hiccup.

You are bound, too, by GitHub’s collaborator model and its rate limits. Access is per GitHub account and depends on the customer accepting an invite; bulk operations answer to GitHub’s API limits. At small and medium scale this is invisible. At very large scale you design around it.

And the setup step that trips people most: the GitHub App. GHRM talks to GitHub through a GitHub App, which needs its PEM private key placed exactly where the plugin expects it, and the App itself needs the right visibility and organisation access. A misnamed PEM or a private App that ought to be public produces invites that quietly 404 with no obvious cause. When something silently refuses to work, this is the first place to look, every time.

None of these are defects. They are the shape of distributing software through GitHub, and GHRM does not paper over them.

Where it leaves you

If your product is code sitting in a private repo, GHRM turns it into a subscription you can actually run: define a catalogue, tie packages to plans, and let subscription events grant and revoke GitHub collaborator access and deploy tokens on their own, with a grace period softening the exit and Actions keeping the catalogue current. Because the core stays agnostic, none of that GitHub logic leaks into your platform; it is one plugin reacting at the edge. You can browse GHRM alongside the rest of the catalogue on the plugins page, and the platform itself is source-available under BSL 1.1, so you can host every part of it yourself and read every line before you trust it with your revenue.

Learn more about VBWD

VBWD is a self-hosted, source-available platform for building subscription products, marketplaces, and AI-powered apps. Explore it further:

VBWD is source-available — get the SDK on GitHub.

Happy
Happy
0%
Sad
Sad
0%
Excited
Excited
0%
Angry
Angry
0%
Surprise
Surprise
0%
Sleepy
Sleepy
0%

Your Checkout Doesn’t Need to Know Who Took the Money

The Front Door Your Best Dataset Never Had

Leave a Reply

Your email address will not be published. Required fields are marked *

3 × three =