Licensing as a core primitive, so any vendor can ship a commercial module and not just us
- Dominant language
- C#
- Stars
- 6
- Forks
- 7
- Avg merge
- 4h 42m
- Merged PRs (30d)
- 307
Description
The plan is one module list where every module carries a free or paid tag, the way the Umbraco marketplace does it. Discovery already supports that: the `barakocms-module` NuGet tag is the mechanism (`CONTRIBUTING.md:113`, `MODULES.md:437`) and `ModuleCatalogue` already reports what is installed and what is running.
What is missing is the runtime half. A module has no way to say it is licensed, and core has no way to check.
## Take the thing Umbraco got wrong
In that ecosystem every commercial vendor writes their own licence check. Umbraco Forms has one, Deploy has another, each third-party vendor writes a third. They are all slightly different and all differently broken, and a customer ends up with a different licence mechanism per package and no single place to see what expires when.
Shipping this once, in the open core, means any vendor can charge for a barakoCMS module without writing that code. That is a reason for somebody to build for this platform rather than another one, and it costs us one primitive.
## What to add
An optional declaration on `IBarakoModule`, defaulting to unlicensed so nothing existing changes:
- Whether the module requires a licence.
- Where its licence is configured, following the existing scoped pattern, so a key sits under `Modules:{Name}` with the module's other settings.
- What core does when the licence is missing, invalid or expired.
Core validates at startup and refuses a module whose licence does not check out, by name, with the reason. That is the same shape as `ModuleContract.MinimumSupported`, which already refuses a module core cannot support rather than loading it and letting it fail somewhere less obvious.
`GET /api/modules` reports licence state per module, so Brew can show it and an operator can see every expiry in one place.
## What has to be decided in this thread
**Offline validation.** A signed licence file checked against a public key needs no network, which matters because a self-hosted instance may have no outbound access and because a licence server that is down must not take a customer's system down with it. I would make offline the only mode and not build a phone-home at all.
**What expiry does.** This is the important one and it is a business decision, not a technical one. A module that stops working on expiry is a hostage situation and will be described that way in public. The defensible norm is that the version already paid for keeps running and updates stop. Whatever is chosen has to be the same for our modules and third-party ones, because core enforces it for both.
**Whether core can refuse at all.** Refusing to start is strong. The alternative is a loud warning and a degraded mode. Refusing is more honest and matches how this codebase treats everything else, and it means a licence bug takes down a production site. Worth arguing rather than assuming.
## Deliberately open
This is core, MPL-2.0, and it must stay that way. A licensing primitive that only we can use is a store. One anybody can use is a marketplace, and the second is worth more.
## Done when
- A module can declare that it needs a licence, and core refuses it when the licence is absent or invalid.
- Validation works with no network access.
- `GET /api/modules` reports licence state and expiry.
- A module that declares nothing behaves exactly as it does today.
- `MODULES.md` documents how a third-party vendor makes their own module commercial.
Contributor guide
Research direction
Start by reading IBarakoModule, ModuleContract.MinimumSupported, ModuleCatalogue, and the existing GET /api/modules entry point. Resolve the open decisions about offline validation, expiry behavior, and startup refusal before implementation. Done means licensed modules are validated and reported, unlicensed existing modules remain unchanged, and MODULES.md documents third-party commercial modules.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100