You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A module refused by plan tier must say so on the instance — today the consumer shows only the consequence (canPatch=False, 'required module not installed') #4097
Measured 2026-09-12 on build.meshweaver.cloud (fresh instance, registered on the free plan via the registry's open-registration key).
What the instance showed, over an hour, through the plugin's Logs/Sample actions and its public /health:
[SelfUpdate] starting … canPatch=False (DetectOnly updater: "list MeshWeaver.SelfUpdate.Aks under Modules:Assemblies for a Kubernetes install");
required_modules: Degraded — 3 required module(s) are store-delivered and not here yet … install the package from the registry, or delist it;
pending_module_activation clearing after a Restart, but the patcher still absent.
What was actually true: the AKS patcher ships in the Hosting package (MeshWeaver.Plugins Hosting/index.json: module: MeshWeaver.SelfUpdate.Aks, preInstalled: true, tier: enterprise). The instance's plan was free, so PluginGrant.Allows → PlanTierRanks.CoversInstance (src/MeshWeaver.Mesh.Contract/Security/PlanTierRanks.cs:121-131, PluginGrant.cs:100-104, InstanceRegistryAuthenticator.cs:723-725) refused the pre-install. By design the refusal is indistinguishable from "no such bundle" on the wire (the enumeration defence, Doc/WhatsNew 2026-08-17) and is logged on the registry, where the consumer cannot read it. memex differs only because it predates the enforcement.
One cause, three symptoms, and nothing on the instance named the cause. We spent the hour on RBAC, then on module delivery, then on a design change. A customer on a lower plan will get exactly this: an instance that boots fine and quietly lacks capabilities.
Ask — the plan-tier refusal must be visible on the consumer, in the vocabulary #4091 introduced for BindingConflict ("⛔ Not installed on this platform: it needs YamlDotNet 18.1.0.0, this platform provides 16.3.0.0"), with different nouns:
⛔ Not installed on this instance: Hosting needs plan tier enterprise, this instance is on free.
Surfaces, in order of value:
/healthrequired_modules: when a required module's package is above the instance's plan, say so instead of "install the package from the registry" (which the instance cannot do).
The package card in the Plugin Catalog / Store listing: show the refused pre-install with the tier sentence, not an absent entry. The enumeration defence is about ungranted sources; a pre-installed package the registry itself declares in the instance's default set is not enumeration.
The self-updater's startup line: when DetectOnly because the patcher's package is tier-refused, name the tier, not Modules:Assemblies.
Mechanically, the registry already knows the verdict at pre-install time (InstanceAutoRegistrationService.cs:1231); the consumer needs it returned in the listing/pre-install response as a typed refusal (package, required tier, instance plan), not as an omission.
Related: #4091 (BindingConflict vocabulary), #4093 (where this was found), MeshWeaver.Plugins#1735 (Sample blind on a new namespace — same "consequence without cause" shape).
Activity
fix(#4097): review — the sentence speaks the ladder's language, a par…