feat: extend premium license for aigovernance (#21499)

Closes [#1227](https://github.com/coder/internal/issues/1227)

Added support for license addons, starting with AI Governance, to enable
dynamic feature grouping without requiring license reissuance.

### What changed?

- Introduced a new `Addon` type to represent groupings of features that
can be added to licenses
- Created the first addon `AddonAIGovernance` which includes AI Bridge
and Boundary features
- Added validation for addon dependencies to ensure required features
are present
- Added new features: `FeatureBoundary` and
`FeatureAIGovernanceUserLimit`
- Updated license entitlement logic to handle addons and their features
- Added helper methods to check if features belong to addons
- Updated tests to verify addon functionality

### Why make this change?

This change introduces a more flexible licensing model that allows
features to be grouped into addons that can be added to licenses without
requiring reissuance when new features are added to an addon. This is
particularly useful for specialized feature sets like AI Governance,
where related features can be bundled together and sold as a separate
SKU. The addon approach allows for better organization of features and
more granular control over entitlements.
This commit is contained in:
Jake Howell
2026-01-27 22:33:53 +11:00
committed by GitHub
parent 1375fd9ead
commit 6f15b178a4
6 changed files with 498 additions and 137 deletions
@@ -185,6 +185,7 @@ type LicenseOptions struct {
// past.
IssuedAt time.Time
Features license.Features
Addons []codersdk.Addon
AllowEmpty bool
}
@@ -225,6 +226,11 @@ func (opts *LicenseOptions) UserLimit(limit int64) *LicenseOptions {
return opts.Feature(codersdk.FeatureUserLimit, limit)
}
func (opts *LicenseOptions) AIGovernanceAddon(limit int64) *LicenseOptions {
opts.Addons = append(opts.Addons, codersdk.AddonAIGovernance)
return opts.Feature(codersdk.FeatureAIGovernanceUserLimit, limit)
}
func (opts *LicenseOptions) ManagedAgentLimit(soft int64, hard int64) *LicenseOptions {
// These don't use named or exported feature names, see
// enterprise/coderd/license/license.go.
@@ -301,6 +307,7 @@ func GenerateLicense(t *testing.T, options LicenseOptions) string {
AllFeatures: options.AllFeatures,
FeatureSet: options.FeatureSet,
Features: options.Features,
Addons: options.Addons,
PublishUsageData: options.PublishUsageData,
}
return GenerateLicenseRaw(t, c)