If you ship software to customers who run it themselves, you already know the licensing bind. You want to sell tiers, bundles, and add-ons. But the customer holds the binary, the database, and root on the box. There is no license server you control at runtime, no phone-home you can guarantee. So how does a self-hosted, source-available platform decide that this deployment may run the booking engine but not the marketplace vendor tools?

VBWD answers this with per-feature licensing: a signed token carries a list of features, and that list decides which plugins a deployment may enable. This article walks through the design — what feature-gating means, how the token drives plugin enablement, why the mechanism lives in the core while the policy lives in a plugin, and where the honest limits are.

What feature-gating actually means here

VBWD is an agnostic-core plugin platform. The core is Python/Flask on Postgres with a Vue 3 admin and user frontend, all in Docker. Everything that has domain meaning — subscriptions, shop, booking, CMS, bots, the marketplace vendor layer — lives in a plugin. The core does not import plugins and holds no plugin vocabulary; an automated oracle fails the build if it ever does.

Feature-gating means a deployment does not get to enable every plugin it happens to have on disk. Enablement is a permission, not a file-presence check. A license token names the features a deployment is entitled to, and the platform refuses to enable plugins whose feature is not in that list. The token is signed with a private key you hold; the deployment verifies it with a bundled public key, entirely offline. No runtime call home, which matters when the whole point of the product is that the customer runs it on their own infrastructure.

This is the same shape as a subscription tier, except the unit is a capability rather than a seat count. An OEM partner who resells VBWD with a "Retail" bundle and a "Services" bundle is really shipping two different feature lists in two different tokens. The code on disk can be identical.

How the token's features list drives enablement

The token is a small signed document. Alongside the usual issuer, subject, and expiry, it carries the field that does the work:

{
  "sub": "acme-oem-deployment-4417",
  "iss": "vbwd-license-authority",
  "exp": "2027-01-01T00:00:00Z",
  "features": ["cms", "shop", "booking", "subscription"],
  "sig": "<ed25519 signature over the canonical payload>"
}

At startup and whenever an operator toggles a plugin, the flow is:

  1. The core reads the token and verifies the signature against the embedded public key. A bad signature, a tampered payload, or an expired token means the features list is not trusted.
  2. When a plugin asks to be enabled, the platform checks whether the plugin's declared feature is present in the verified list.
  3. If it is, the plugin enables normally: its blueprint mounts, its DI providers register, its migrations are expected to have run. If it is not, enablement is refused at the seam.

The important detail is where the check sits. It is not a banner in the admin UI that a determined operator scrolls past. It is at the enablement seam — the same seam every plugin already passes through to register routes and services. A plugin that is not entitled does not get its providers wired, so its capability is not merely hidden; it is not constructed.

OEM partners get a clean operational model out of this. To change what a customer can run, you reissue a token with a different feature list and drop it in. You do not rebuild an image, fork a branch, or maintain a "lite" edition. The pricing tier and the technical entitlement are the same object.

Mechanism in core, policy in plugin

Here is the constraint that shapes the whole design. The core is not allowed to know what "shop" or "booking" means. If the core held a table mapping feature names to plugin domains — "the marketplace feature unlocks the vendor plugin, which costs this much" — that is plugin vocabulary sitting in the core, and the oracle would fail the build. It would also be the wrong place for it: pricing and bundling are business policy that changes far more often than the verification mechanism.

So the responsibility splits:

The mental model already exists elsewhere in VBWD. The route-audit oracle is a core mechanism that enforces a policy — which routes may be publicly exposed — without the core itself deciding the business meaning of any given route; the allowlist is declared, and the mechanism enforces it. Feature-gating is the same move: generic enforcement in the core, specific policy declared from outside. Keeping that line clean is exactly what keeps the agnostic-core oracle green while still letting the platform sell capabilities.

This separation is also what makes per-feature licensing the revenue mechanism for the marketplace and the managed edition. Paid plugins are just plugins whose feature is not in a free deployment's default token. The hub decides pricing and issues tokens; the core only ever checks a signature and compares strings.

A worked example

Take an OEM partner, Acme, reselling VBWD to service businesses. Acme sells two bundles. The Standard bundle includes CMS, shop, and subscriptions. The Pro bundle adds the booking engine and the marketplace vendor tools.

A Standard customer receives a token with features: ["cms", "shop", "subscription"]. On that box, the booking plugin can sit on disk — the image is identical to a Pro box — but when an operator tries to enable it, the core checks the booking plugin's declared feature against the verified list, does not find it, and refuses enablement. The plugin's blueprint does not mount and its services are never constructed. The admin sees the plugin listed as available-but-not-entitled, with a pointer to upgrade.

The customer upgrades to Pro. Acme reissues a token: features: ["cms", "shop", "subscription", "booking", "marketplace"]. The operator drops the new token in and restarts the API. Now the booking and marketplace plugins pass the entitlement check at the seam, enable, register their providers, and run. No new code shipped. The only thing that changed is a signed list.

From Acme's side, the token issuance and the feature-to-plugin mapping live in the hub plugin, not in any customer's core. Acme changes a bundle definition once, in one place, and every future token reflects it. The operator documentation covers the mechanics of installing and rotating a token on a running deployment.

Honest limits

This needs to be stated plainly, because the product is source-available under BSL 1.1 — not a black box, and never CC0. A customer, or a determined third party, has the source. That has consequences that no amount of cryptography changes:

It is also worth being clear about status. Per-feature licensing is VBWD's designed model, and parts of it are still being built. Treat this as an architecture description, not a shipped DRM guarantee. The design goal is honest by construction: make forgery cryptographically impossible, make removal costly through entanglement, and rely on the license as the final line — rather than pretending a self-hosted binary can be made tamper-proof.

Takeaway

Per-feature licensing lets a source-available platform sell capabilities the way a SaaS sells tiers, without a runtime license server. A signed, offline-verified token carries a feature list; that list decides which plugins a deployment may enable, enforced at the same seam every plugin already passes through. The verification mechanism lives in the agnostic core so it stays generic and the oracle stays green; the policy — feature-to-plugin mapping, bundling, pricing — lives in a plugin where domain knowledge belongs. For OEM partners, changing what a customer can run is a token swap, not a rebuild. The honest framing is that cryptography prevents token forgery, entanglement makes checks costly to remove, and the license is the ultimate backstop — designed architecture, not a tamper-proof promise.

Building on VBWD or reselling it? Start with the architecture overview and the plugin catalogue to see which capabilities map to which features.