Is your feature request related to a problem? Please describe.
module.info describes the basics of a module.
It is currently possible to require a specific library or other module, but this mechanism seems prone to errors and lacks some features.
There is no mechanism to express:
- Which PHP version a module needs
- Which PHP extensions need to be active
- Which version of icingaweb a module is compatible with
- State that this module is incompatible with another module
- Add "optional dependencies" or "suggestions" for other modules
- Define "replacements" when when a package can stand in for a library. See "icinga-php-library" vs "ipl-*"
- There is no way for a module to communicate that it is abandoned/deprecated or that it has been superseded by another module. See: "monitoring"
As a result, modules are just assumed to be ok to be loaded and are can be activated even when their requirements are not met, leading to runtime errors or undefined behavior that break icingaweb to the point of complete breakdown.
Describe the solution you'd like
A metadata file per module that declares its requirements just like the existing module.info file, but in a more extensible format (JSON/YAML).
This file is read when the state of modules change and the resulting dependency tree is resolved.
Modules that do not satisfy their requirements are not activated.
Activation failures cascade: if module A depends on module B and B fails to activate, A also fails.
Why YAML
YAML is the primary format for the metadata file for the following reasons:
- Comments: module authors can document why a dependency exists or why a specific version range is required, directly in the file.
- Readability: the format is compact and human-friendly without the punctuation overhead of JSON (no closing braces, minimal quoting).
- Native array support: the OR constraint syntax relies on YAML sequences, which map naturally to the format without any additional syntax invention.
- Familiarity: YAML is already widely used as a configuration format.
JSON is also accepted as an alternative for tooling that generates metadata programmatically, since it is easier to produce without a dedicated serializer.
But lacks support for comments, has higher overhead and is slightly harder to read by humans.
INI was considered but rejected due to limited nesting support and no native array type, both of which are required by this format.
Metadata fields
Top-level:
- name: module identifier
- version: module version
- description: human-readable description
- abandoned:
false (default), true, or a string pointing to a replacement
depends: hard requirements. If any are unmet the module does not activate.
Supported keys:
- php: required PHP version
- icingaweb: required Icinga Web version
ext-*: required PHP extensions
- libraries: map of library names to version constraints
- modules: map of module names to version constraints
suggests: optional enhancements. Does not affect activation, but is surfaced in the Icinga Web modules configuration when the suggested module is not active. Each entry under modules is either a reason string or an object with an optional version constraint and a reason string.
conflicts: modules or libraries that cannot be active at the same time as this one. Accepts libraries and modules sub-sections.
replaces: declares that this module provides specific libraries or supersedes specific modules at given versions, satisfying any dependencies on those targets. Accepts libraries and modules sub-sections. Implicitly conflicts with all replaced targets.
Version constraint operators
| Operator |
Meaning |
* |
Any version |
=, == |
Exact match |
>= |
Greater than or equal |
<= |
Less than or equal |
> |
Strictly greater than |
< |
Strictly less than |
^ |
SemVer-compatible range (^ 1.2.3 = >= 1.2.3, < 2.0.0) |
~ |
SemVer-compatible minor range (~ 2.13.2 = >= 2.13.2, < 2.14.0) |
^ has special handling for versions starting with "0". In this case ^0.1.2 = >= 0.1.2, < 0.2.0.
PHP's built-in version_compare function is suitable for evaluating most of these operators.
Combining constraints
Multiple constraints can be combined using AND and OR logic.
AND: comma-separated within a single string.
All constraints must be satisfied:
ipl-web: ">= 1.0.0, < 2.0.0"
OR: an array of constraint strings.
At least one item must be satisfied:
ipl-web:
- ">= 1.0.0, < 2.0.0"
- ">= 3.0.0"
Each array item is itself an AND group, making the full structure Disjunctive Normal Form.
A plain string is treated as a single-element OR group internally.
Examples
name: my-module
version: 1.3.0
description: Provides monitoring dashboards for infrastructure metrics
depends:
php: ">= 8.1"
icingaweb: ">= 2.4.0"
ext-json: "*"
ext-curl: ">= 7.0"
libraries:
icinga-php-library:
- ">= 1.0.5, < 1.2.5"
- ">= 2.1.0"
modules:
some-other-module: ">= 1.0.0"
suggests:
modules:
optional-module: Extends details page with interesting information
another-module:
version: ">= 1.5.0"
reason: Enables PDF export for dashboard reports
conflicts:
modules:
legacy-module: "*"
Module acting as a drop-in replacement for another:
name: feeds
version: 1.2.0
replaces:
modules:
rss: "*"
An abandoned module pointing to its migration guide.
name: legacy-module
version: 1.2.3
abandoned: "https://icinga.com/docs/icinga-db-web/latest/doc/10-Migration/"
Activation algorithm
- List all enabled modules
- For each module: check its own requirements (
php, icingaweb, ext-*, libraries, conflicts) — mark as failed if unmet
- Propagate failures: any module whose
depends target has failed also fails
- Repeat step 3 until no new failures occur
- Activate all modules not marked as failed
Describe alternatives you've considered
Flat requirement lists without propagation: a simple pass/fail check per module without graph traversal would miss the case where module A is technically satisfied but depends on module B which is itself broken. Both should fail together.
Using OR inside the version string: considered a dedicated || operator inside the constraint string (as npm does). Rejected in favor of using a YAML/JSON array, which is more readable and quicker to parse.
A flat dependency map without sub-sections: considered using a single flat map for all dependency types under depends, relying on naming conventions or a registry lookup to distinguish libraries from modules. Rejected because Icinga Web cannot determine the type of a missing dependency from the registry alone, which would prevent meaningful error messages from being shown to the user. Explicit libraries and modules sub-sections make the type unambiguous regardless of whether the target is installed.
Additional context
replaces implicitly creates a conflicts relationship: a bundle and the individual packages it replaces cannot be active simultaneously.
suggests without a version uses *: The reason field is surfaced in the UI to explain what capability the suggested module unlocks.
- The
abandoned field is surfaced in the UI: true shows a generic warning, a string value links to or names the recommended replacement.
name could be a name spaced identifier so modules from different vendors don't collide by accident.
Open issues
- This proposal doesn't handle the case where there are multiple possible options for the same dependency. Example:
businessprocess requires one of icingadb or monitoring.
It is possible to define both as suggests but the module really does require at least one of them to be useful at all.
Is your feature request related to a problem? Please describe.
module.infodescribes the basics of a module.It is currently possible to require a specific library or other module, but this mechanism seems prone to errors and lacks some features.
There is no mechanism to express:
As a result, modules are just assumed to be ok to be loaded and are can be activated even when their requirements are not met, leading to runtime errors or undefined behavior that break icingaweb to the point of complete breakdown.
Describe the solution you'd like
A metadata file per module that declares its requirements just like the existing
module.infofile, but in a more extensible format (JSON/YAML).This file is read when the state of modules change and the resulting dependency tree is resolved.
Modules that do not satisfy their requirements are not activated.
Activation failures cascade: if module A depends on module B and B fails to activate, A also fails.
Why YAML
YAML is the primary format for the metadata file for the following reasons:
JSON is also accepted as an alternative for tooling that generates metadata programmatically, since it is easier to produce without a dedicated serializer.
But lacks support for comments, has higher overhead and is slightly harder to read by humans.
INI was considered but rejected due to limited nesting support and no native array type, both of which are required by this format.
Metadata fields
Top-level:
false(default),true, or a string pointing to a replacementdepends: hard requirements. If any are unmet the module does not activate.
Supported keys:
ext-*: required PHP extensionssuggests: optional enhancements. Does not affect activation, but is surfaced in the Icinga Web modules configuration when the suggested module is not active. Each entry under
modulesis either a reason string or an object with an optionalversionconstraint and areasonstring.conflicts: modules or libraries that cannot be active at the same time as this one. Accepts
librariesandmodulessub-sections.replaces: declares that this module provides specific libraries or supersedes specific modules at given versions, satisfying any dependencies on those targets. Accepts
librariesandmodulessub-sections. Implicitly conflicts with all replaced targets.Version constraint operators
*=,==>=<=><^^ 1.2.3=>= 1.2.3, < 2.0.0)~~ 2.13.2=>= 2.13.2, < 2.14.0)^has special handling for versions starting with "0". In this case^0.1.2=>= 0.1.2, < 0.2.0.PHP's built-in
version_comparefunction is suitable for evaluating most of these operators.Combining constraints
Multiple constraints can be combined using AND and OR logic.
AND: comma-separated within a single string.
All constraints must be satisfied:
OR: an array of constraint strings.
At least one item must be satisfied:
Each array item is itself an AND group, making the full structure Disjunctive Normal Form.
A plain string is treated as a single-element OR group internally.
Examples
Module acting as a drop-in replacement for another:
An abandoned module pointing to its migration guide.
Activation algorithm
php,icingaweb,ext-*, libraries, conflicts) — mark as failed if unmetdependstarget has failed also failsDescribe alternatives you've considered
Flat requirement lists without propagation: a simple pass/fail check per module without graph traversal would miss the case where module A is technically satisfied but depends on module B which is itself broken. Both should fail together.
Using OR inside the version string: considered a dedicated
||operator inside the constraint string (as npm does). Rejected in favor of using a YAML/JSON array, which is more readable and quicker to parse.A flat dependency map without sub-sections: considered using a single flat map for all dependency types under
depends, relying on naming conventions or a registry lookup to distinguish libraries from modules. Rejected because Icinga Web cannot determine the type of a missing dependency from the registry alone, which would prevent meaningful error messages from being shown to the user. Explicitlibrariesandmodulessub-sections make the type unambiguous regardless of whether the target is installed.Additional context
replacesimplicitly creates aconflictsrelationship: a bundle and the individual packages it replaces cannot be active simultaneously.suggestswithout a version uses*: Thereasonfield is surfaced in the UI to explain what capability the suggested module unlocks.abandonedfield is surfaced in the UI:trueshows a generic warning, a string value links to or names the recommended replacement.namecould be a name spaced identifier so modules from different vendors don't collide by accident.Open issues
businessprocessrequires one oficingadbormonitoring.It is possible to define both as
suggestsbut the module really does require at least one of them to be useful at all.