Goal: issue signed license keys that decide which plugins a customer's deployment is allowed to enable — so you can sell a source-available product and still gate premium features by what someone paid for.

Who this is for (and the honest tension)

This is for anyone shipping software to customers who run it themselves — an OEM, a vendor selling an on-premise product, a platform business with a paid tier — who needs "you bought Plan Pro, so these plugins turn on" to hold even though the code is on the customer's server. There's an honest tension to name up front: if your software is source-available, a determined customer can read the code and remove the gate. Per-feature licensing is not DRM and won't stop a motivated bad actor. What it does is make the honest path the easy path — legitimate customers get exactly what they paid for, automatically, and enabling something they didn't buy takes deliberate tampering that breaks their support and their license. For most B2B software that's the right trade; if you need hard copy-protection, this isn't it.

Prerequisites

Step 1 — Stand up the License Server (the authority)

The License Server is the single place that holds the private key and issues signed licenses. Treat that key like a production secret — it never leaves the authority, and anyone who has it can mint valid licenses. Deploy the license_authority bundle on your hub instance and load the private key from the environment, not the repo.

The authority exposes three operations: sign (produce a license), issue (bind it to a customer/plan), and revoke (invalidate one). Keep the authority and the client on separate deployments — co-locating the signer and the verifier defeats the point, because the thing checking the license would sit next to the thing that can forge one.

Success looks like: the authority can sign a test license and the signature verifies against the public key; the private key exists only on the hub.

Step 2 — Define license plans with a feature list

A license is the machine-readable version of a plan. Define tariff plans where each carries a features: [...] array — the list of capabilities (typically plugin ids) that plan unlocks. For example, a Pro plan might carry ["shop", "bookings", "mcp"] while Starter carries only ["shop"].

The feature list is the contract. When the authority issues a license for a customer's plan, it embeds that plan's feature array into the signed payload. Keep the feature ids stable — renaming a feature id after you've issued licenses that reference it is how you accidentally lock paying customers out.

Success looks like: issuing a license for the Pro plan produces a signed token whose payload lists exactly Pro's features.

Step 3 — Ship the License Client in customer instances

Every customer deployment runs the License Client: the license_gate in core, plus your public key (safe to distribute — it can only verify, never sign). The client verifies a license offline — it checks the Ed25519 signature against the bundled public key without calling home on every request. That matters for on-prem software: the gate keeps working if the customer's server can't reach your authority, and you're not adding a network dependency to every plugin load.

Gotcha: offline verification means revocation isn't instant. A revoked license keeps verifying until the client re-checks (on renewal, or on a periodic pull). Decide your revocation window — how stale a license may be before the client refuses it — and document it. Short window = tighter control but more phone-home; long window = more resilient but slower to cut off.

Step 4 — Gate plugin enablement by entitlement

Now connect the license to behaviour. The license_gate checks, at plugin-enable time, whether the current license's feature list includes that plugin. If the license grants bookings, the bookings plugin enables; if it doesn't, the plugin stays off — and the admin sees why, rather than a silent failure.

Because this rides the plugin system's existing enable path, you're not scattering license checks through feature code. The gate lives at the seam where a plugin turns on, so a plugin's own code doesn't need to know licensing exists.

Success looks like: a Starter license cannot enable a Pro-only plugin (the enable attempt is refused with a clear reason), while a Pro license enables it cleanly.

Step 5 — Self-serve key management

Close the loop so you're not emailing license files by hand. Customers manage their key from dashboard/plan/<slug>: they see their current plan, retrieve or rotate their license key, and — because the license is tied to their subscription — an upgrade re-issues a license with the new feature set automatically. Buy Pro, the plan changes, a new license is minted, the extra plugins become enable-able.

Success looks like: a customer upgrading their plan receives an updated license without you touching anything, and the newly-entitled plugins can now be enabled on their instance.

Verification checklist

Limits & next steps

This is entitlement, not copy-protection. Source-available means the gate is readable and, in principle, removable by someone determined. Licensing makes the legitimate path frictionless and tampering deliberate and support-voiding; it does not make the code uncrackable. Price and position accordingly.

Offline verification trades immediacy for resilience. Revocation isn't instant by design. That's usually the right call for on-prem, but know the window and make sure it fits your risk.

The private key is the whole system. If it leaks, every license you've ever issued is forgeable and you're rotating keys and re-issuing to every customer. Guard it like the crown jewel it is.

Takeaway

Selling gated features on software the customer runs sounds contradictory until you separate the pieces: an authority that holds a private key and signs licenses, plans that carry a feature list, and a client that verifies offline against a public key and gates plugin enablement by entitlement. The customer buys a plan, gets a signed key, and only their entitled plugins turn on — with upgrades re-issuing automatically. It's honest-path licensing for source-available software, not DRM, and that distinction is a feature: your legitimate customers get exactly what they paid for without friction. Under BSL 1.1 (free for commercial use while annual VBWD-attributable sales stay under the value of 6.7 BTC per year), you can build this monetization on top of a platform you own outright.

Building a licensed product? See the licensing details in the developer docs, the plugin catalogue, and the ownership model in pricing.