Format license utils package
This commit fixes an issue where the files added when license retrieval moved into a shared utils package were never run through ruff format, so the formatter check fails on them. Reformatting only reflows multi-line collection literals and parametrize arguments to match line length; no logic changes.
Add missing coverage for NFS snapshot entitlement
This commit adds changes to register the bespoke NFS snapshot denial message for all three deny reasons rather than just the two currently reachable, so editing a matrix cell later cannot silently drop the wording back to the generic template.
It also covers the feature against the live policy instead of only the matrix fixture, and reworks the unentitled integration fixture to mock the license rather than the entitlement check, so the negative path exercises the real engine instead of asserting a string the test itself handed in.
Gate SMB fast path and Veeam shares through the entitlement engine
This commit adds changes to gate the SMB ZFS fastpath parameters and Veeam repository shares through the entitlement engine instead of a bare is_enterprise read. Both stay separate flags with their own matrix vectors and remain in the enterprise-only legacy injection bucket, so existing licensed systems keep both features and only unlicensed HA-capable boxes lose them.
Flip NVMe-oF SPDK onto its matrix vector
This commit adds changes to move NVMEOF_SPDK off its transitional legacy rule and onto its matrix vector, which drops the is_ha_capable disjunct. That was a licensing proxy inherited from product_type rather than a physical prerequisite, so HA capable systems holding no feature key no longer get SPDK without a license.
The key is now injected into every legacy license instead of only enterprise models, so existing legacy holders keep SPDK on upgrade.
Gate TrueSearch through the entitlement engine
This commit adds changes to route the TrueSearch gate through the entitlement engine and drop the TrueNAS Connect disjunct, since a system that connects to TNC is issued a license carrying the key rather than being entitled by the connection itself. This also tightens the check from mere license presence to the feature key, which is what the feature matrix asks for, and the denial reason now comes from the entitlement instead of a hardcoded string naming TNC as an alternative.
Gate Webshare through the entitlement engine
This commit adds changes to gate Webshare through the entitlement engine. Webshare had no license check at all until now, so the key is injected into every legacy license to make sure no existing licensee loses their shares on upgrade.
Enforcement is set-time only, on share creation and on the service config. An update that leaves a share disabled is let through without the entitlement, so a system that has lost it can still turn a share off rather than having to delete it. Existing shares keep serving, since nothing re-reads the entitlement after the fact.
Note the WebUI currently hides Webshare from Enterprise systems and shows it to Community Edition, which is the inverse of this gate. That needs a matching change on the UI side before the two agree.
Gate directory services authentication through the entitlement engine
This commit adds changes to gate ds_auth, which controls whether directory services accounts may authenticate to the UI and API, through the entitlement engine instead of a bare is_enterprise read. Directory services themselves stay completely ungated on every product including Community Edition, so AD, LDAP and IPA are unaffected and only the UI and API login path is licensed. DIRECTORY_SERVICES also moves into the unconditional legacy injection bucket so every legacy licensee keeps it regardless of model.
Gate STIG and FIPS mode through the entitlement engine
This commit adds changes to route the security config gate through the entitlement engine instead of checking for the mere presence of a license. FIPS has no key of its own so the STIG entitlement governs every enterprise security option, matching the single STIG row in the feature matrix. The check stays on the write path only, since consuming it on the read path would silently unharden PAM on the next login.
Gate feature checks through the entitlement engine
This commit adds changes to route the dedup, SED, NVMe-oF SPDK, ZFS tiering and proactive support gates through truenas.entitlements.check instead of hand-composed license predicates scattered across plugins. It also fixes a crash in product_type when a license carries no hardware model, which now classifies as Community Edition.
Gate KMIP through the entitlement engine
This commit adds changes to gate KMIP key management through the entitlement engine. KMIP had no license check at all until now, so the key is injected into every legacy license to make sure no existing licensee loses key management on upgrade.
Only the enable transition is gated. Disabling KMIP is how escrowed ZFS and SED keys are pulled back to the local database, so a system that loses the entitlement must still be able to turn it off and recover its keys.
Gate apps, containers and VMs through the entitlement engine
This commit adds changes to route the apps, containers and VMs gates through the entitlement engine rather than raw feature flag reads, and injects those keys into legacy licenses so existing licensees keep both capabilities after upgrade. Note this means an injected key now overrides the legacy jails/vm bits on HA capable hardware, which is called out in a TODO next to the injection bucket.
Inject feature keys when translating legacy licenses
This commit adds changes to normalize legacy licenses at the translation layer by injecting the feature keys a legacy holder is entitled to today: capabilities gated on any valid license go to every legacy license, is_enterprise-gated capabilities go to enterprise models only, and CONTAINERS rides along with the legacy jails bit. This keeps backwards compatibility in one place so gates can evaluate the new key vocabulary uniformly.
Expose per-feature source and tier on license info
This commit adds changes to carry each licensed feature's source and tier qualifier on FeatureInfo, so entitlement rules can consume per-feature tiers (e.g. the SUPPORT contract tier) without reaching into raw license payloads.
Gate FEC mode configuration through the entitlement engine
This commit adds changes to gate FEC mode configuration on the entitlement engine instead of system.is_enterprise. The physical interface check and the ethtool capability probe stay where they are, since those are hardware facts the engine does not model.
NETWORK_FEC is now injected into every legacy license rather than only enterprise models, so HA capable systems whose license carries no model or a freenas prefixed one keep being able to configure it.
Gate NFS snapshot exposure through the entitlement engine
This commit adds changes to gate NFS snapshot exposure on the entitlement engine instead of system.is_enterprise, keeping the existing validation wording via a per-feature message override.
NFS_SNAPSHOT is now injected into every legacy license rather than only enterprise models. Its matrix vector is key-only on both hardware sides, so a legacy holder whose model is freenas-prefixed would otherwise lose the export on upgrade. The trade-off is that freenas certified systems, which are denied today, gain the feature.
Add license entitlement engine
This commit adds a pure entitlement engine that resolves whether a feature is available from hardware class and license facts, using per-feature policy rules: product-matrix vectors, a support-tier rule, a license-type rule for HA, and transitional legacy rules that reproduce current gate behavior until each feature is flipped onto its matrix vector. The full product matrix ships as reference data, and completeness tests keep the policy, matrix, display names and API vocabulary in sync so a new feature flag cannot silently skip a site.
Gate NVMe expansion shelves through the entitlement engine
This commit adds changes to gate JBOF expansion shelves through the entitlement engine rather than a bare enclosure count, keeping the shelf count itself as a conjunct and dropping the system serial check, which is a license to machine binding the license layer already owns. Legacy licenses have no way to carry a JBOF key since shelf ownership lives only in the additional hardware list, so the key is injected from the ES24N enclosure entry and licenses without a shelf are left untouched.
Move license retrieval into a shared utils package
This commit adds changes to move license retrieval and legacy license normalization out of plugins/truenas and into middlewared.utils.license, so there is a single get_license() that every consumer reads and one normalized shape it returns regardless of whether the answer came from the license daemon or the legacy on-disk blob. is_licensed_for_ha had quietly grown its own composition of the same two normalizers with a different fallback rule, which is what this collapses.
There is one behaviour change worth calling out: a v2 license that exists but fails verification is now authoritative and no longer falls back to the legacy blob, while a wedged or erroring daemon does now fall back instead of reporting the system unlicensed. LicenseInfo also splits its conflated expires_at into separate support and license expiry fields and keys features by name rather than flattening them into a list. The public truenas.license.info payload is unchanged and is now pinned byte for byte by an explicit projection and a golden test, since it is still an untyped dict and nothing else held its shape.
Gate RDMA through the entitlement engine
This commit adds changes to make rdma.capable_protocols the single place RDMA availability is decided, so it consults the entitlement engine instead of system.is_enterprise. The chassis check that refused RDMA to Minis is dropped, since the matrix entitles a Mini whose license carries the key, and the NIC probe stays because that is a hardware fact the engine does not model.
Every other RDMA decision site now routes through that chokepoint via a per module capability helper, so a validator and its runtime mask can no longer drift apart. That drift is how NFS ended up masking on is_enterprise while its own validator checked for a capable NIC. The LIO renderer also stops reading the raw iser bit and consumes the gated method instead, which it never did.
RDMA is injected into every legacy license rather than only enterprise models, so existing licensees keep it.
Fix formatting in entitlement plugin unit tests
This commit makes some changes where ruff formatting had not been applied to the plugin unit tests added earlier on this branch, so the suite now passes ruff format cleanly.
Gate Fibre Channel through the entitlement engine
This commit adds changes to route both Fibre Channel gates through the entitlement engine instead of raw feature flag reads, and drops the is_enterprise conjunct from fc.capable since the feature matrix already denies the community edition columns and was the only thing blocking a CE system that holds the key. HBA presence stays at the call site as it is a hardware fact the entitlement does not model.
Add truenas.entitlements service
This commit adds a private service that gathers hardware and license facts and evaluates feature entitlements through the entitlement engine, giving gates a single typed call to answer whether a feature is available.
Widen system.feature_enabled to the full feature vocabulary
This commit adds changes to accept every known license feature key in system.feature_enabled now that the license vocabulary covers all gated features.