diff --git a/public_html/appmonitor/classes/appmonitor-checks.class.php b/public_html/appmonitor/classes/appmonitor-checks.class.php
index 865a0e8e041ca1cc0f46d13c2db5aa8d4a8f2d37..4aa9deead4e675692bad5b0cf571197e798d7094 100755
--- a/public_html/appmonitor/classes/appmonitor-checks.class.php
+++ b/public_html/appmonitor/classes/appmonitor-checks.class.php
@@ -1,4 +1,5 @@
 <?php
+require_once 'validateparam.class.php';
 
 if (!defined('RESULT_OK')) {
     define("RESULT_OK", 0);
@@ -36,24 +37,28 @@ if (!defined('RESULT_OK')) {
  * --------------------------------------------------------------------------------<br>
  * <br>
  * --- HISTORY:<br>
- * 2014-10-24  0.5   axel.hahn@iml.unibe.ch<br>
- * 2015-04-08  0.9   axel.hahn@iml.unibe.ch  added sochket test: checkPortTcp<br>
- * 2018-06-29  0.24  axel.hahn@iml.unibe.ch  add file and directory checks<br>
- * 2018-07-17  0.42  axel.hahn@iml.unibe.ch  add port on mysqli check<br>
- * 2018-07-26  0.46  axel.hahn@iml.unibe.ch  fix mysql connection check with empty port param<br>
- * 2018-08-14  0.47  axel.hahn@iml.unibe.ch  appmonitor client: use timeout of 5 sec for tcp socket connections<br>
- * 2018-08-15  0.49  axel.hahn@iml.unibe.ch  cert check: added flag to skip verification<br>
- * 2018-08-23  0.50  axel.hahn@iml.unibe.ch  replace mysqli connect with mysqli real connect (to use a timeout)<br>
- * 2018-08-27  0.52  axel.hahn@iml.unibe.ch  add pdo connect (starting with mysql)<br>
- * 2018-11-05  0.58  axel.hahn@iml.unibe.ch  additional flag in http check to show content<br>
- * 2019-05-31  0.87  axel.hahn@iml.unibe.ch  add timeout as param in connective checks (http, tcp, databases)<br>
- * 2019-06-05  0.88  axel.hahn@iml.unibe.ch  add plugins<br>
- * 2021-10-28  0.93  axel.hahn@iml.unibe.ch  add plugins<br>
- * 2021-12-14  0.93  axel.hahn@iml.unibe.ch  split plugins into single files; added key group in a check<br>
- * 2023-06-02  0.125 axel.hahn@unibe.ch      replace array_key_exists for better readability
- * 2024-07-22  0.137 axel.hahn@unibe.ch      php 8 only: use typed variables
+ * 2014-10-24  0.5    axel.hahn@iml.unibe.ch<br>
+ * 2015-04-08  0.9    axel.hahn@iml.unibe.ch  added sochket test: checkPortTcp<br>
+ * 2018-06-29  0.24   axel.hahn@iml.unibe.ch  add file and directory checks<br>
+ * 2018-07-17  0.42   axel.hahn@iml.unibe.ch  add port on mysqli check<br>
+ * 2018-07-26  0.46   axel.hahn@iml.unibe.ch  fix mysql connection check with empty port param<br>
+ * 2018-08-14  0.47   axel.hahn@iml.unibe.ch  appmonitor client: use timeout of 5 sec for tcp socket connections<br>
+ * 2018-08-15  0.49   axel.hahn@iml.unibe.ch  cert check: added flag to skip verification<br>
+ * 2018-08-23  0.50   axel.hahn@iml.unibe.ch  replace mysqli connect with mysqli real connect (to use a timeout)<br>
+ * 2018-08-27  0.52   axel.hahn@iml.unibe.ch  add pdo connect (starting with mysql)<br>
+ * 2018-11-05  0.58   axel.hahn@iml.unibe.ch  additional flag in http check to show content<br>
+ * 2019-05-31  0.87   axel.hahn@iml.unibe.ch  add timeout as param in connective checks (http, tcp, databases)<br>
+ * 2019-06-05  0.88   axel.hahn@iml.unibe.ch  add plugins<br>
+ * 2021-10-28  0.93   axel.hahn@iml.unibe.ch  add plugins<br>
+ * 2021-12-14  0.93   axel.hahn@iml.unibe.ch  split plugins into single files; added key group in a check<br>
+ * 2023-06-02  0.125  axel.hahn@unibe.ch      replace array_key_exists for better readability
+ * 2024-07-22  0.137  axel.hahn@unibe.ch      php 8 only: use typed variables
+ * 2025-02-28  0.152  axel.hahn@unibe.ch      listChecks: add loop over currently loaded classes
+ * 2025-03-03  0.153  axel.hahn@unibe.ch      getSize() preg_replace did not work in compiled binary
+ * 2025-03-04  0.154  axel.hahn@unibe.ch      finish with existcode instead of die()
+ * 2025-03-18  0.156  axel.hahn@unibe.ch      add validation class
  * --------------------------------------------------------------------------------<br>
- * @version 0.137
+ * @version 0.156-dev
  * @author Axel Hahn
  * @link TODO
  * @license GPL
@@ -66,6 +71,47 @@ class appmonitorcheck
     // CONFIG
     // ----------------------------------------------------------------------
 
+    protected array $_aCheckDocs = [
+        'name' => [
+            'type' => 'string',
+            'required' => true,
+            'description' => 'Name of a check',
+            'regex'=>'/./',
+            'example' => 'configfile',
+        ],
+        'description' => [
+            'type' => 'string',
+            'required' => true,
+            'description' => 'A short description to describe what is tested',
+            'regex'=>'/./',
+            'example' => 'Check if config file inc_config.php exists, is readable and writable',
+        ],
+        'check' => [
+            'type' => 'array',
+            'required' => true,
+            'description' => 'Array of the check',
+        ],
+        'parent' => [
+            'type' => 'string',
+            'required' => false,
+            'description' => 'Reference a \'name\' of another check to generate a dependency tree',
+            'regex'=>'/./',
+        ],
+        'group' => [
+            'type' => 'string',
+            'required' => false,
+            'description' => 'Name of a group to be nedsted in',
+            'regex'=>'/./',
+        ],
+        'worstresult' => [
+            'type' => 'int',
+            'required' => false,
+            'description' => 'A failed check is max counted as given result. Use it on not required but optional checks',
+            'min'=>RESULT_OK,
+            'max'=>RESULT_ERROR,
+        ],
+    ];
+
     /**
      * starting time using microtime
      * @var float
@@ -118,6 +164,25 @@ class appmonitorcheck
     // PRIVATE FUNCTIONS
     // ----------------------------------------------------------------------
 
+    /**
+     * Exit execution with error message and given exicode.
+     * @param int    $iHttpcode  http statuscode
+     * @param string $sMessage   detailed message
+     * @param int    $iExitcode  exitcode
+     * @return never
+     */
+    protected function _exit($iHttpcode, $sMessage, $iExitcode): never
+    {
+        $aStatus=[
+            503 => 'Service Unavailable',
+        ];
+        header("HTTP/1.1 $iHttpcode $aStatus[$iHttpcode]");
+        echo "<h1>$iHttpcode $aStatus[$iHttpcode]</h1>
+            <h2>Details</h2>
+            $sMessage\n";
+        exit($iExitcode);
+    }
+
     /**
      * Internal: create basic array values for metadata
      * @return boolean
@@ -195,36 +260,52 @@ class appmonitorcheck
     /**
      * Check a given array if it contains wanted keys
      * @param array  $aConfig   array to verify
-     * @param string $sKeyList  key or keys as comma seprated list 
+     * @param string $sMustKeys  key or keys as comma seprated list 
      * @return boolean
      */
-    protected function _checkArrayKeys($aConfig, $sKeyList)
+    protected function _checkArrayKeys($aConfig, $sMustKeys, $sOptionalKeys = '')
     {
-        foreach (explode(",", $sKeyList) as $sKey) {
+        $aTmp=$aConfig;
+        foreach (explode(",", $sMustKeys) as $sKey) {
             if (!isset($aConfig[$sKey])) {
-                header('HTTP/1.0 503 Service Unavailable');
-                die('<h1>503 Service Unavailable</h1>'
-                    . '<h2>Details</h2>'
-                    . __METHOD__ . " - array of check parameters requires the keys [$sKeyList] - but key <code>$sKey</code> was not found in config array."
-                    . "<pre>" . print_r($aConfig, true) . '</pre>'
+                $this->_exit(
+                    503, 
+                    __METHOD__ . " - array of check parameters requires the keys [$sMustKeys] - but key <code>$sKey</code> was not found in config array."
+                        . "<pre>" . print_r($aConfig, true) . '</pre>',
+                    20
                 );
             }
             if (is_null($aConfig[$sKey])) {
-                header('HTTP/1.0 503 Service Unavailable');
-                die('<h1>503 Service Unavailable</h1>'
-                    . '<h2>Details</h2>'
-                    . __METHOD__ . " - key <code>$sKey</code> is empty in config array"
-                    . "<pre>" . print_r($aConfig, true) . '</pre>'
+                $this->_exit(
+                    503,
+                    __METHOD__ . " - key <code>$sKey</code> is empty in config array"
+                        . "<pre>" . print_r($aConfig, true) . '</pre>',
+                    21
                 );
             }
+            unset($aTmp[$sKey]);
         }
+
         return true;
     }
 
+ 
+
     // ----------------------------------------------------------------------
     // PUBLIC FUNCTIONS
     // ----------------------------------------------------------------------
 
+    /**
+     * Self documentation of a check. The array is defined in 
+     * plugins/checks/*.php files
+     * 
+     * @return array
+     */
+    public function explain(): array
+    {
+        return $this->_aDoc??[];
+    }
+
     /**
      * Perform a check
      * @param array $aConfig  configuration array for a check, eg.
@@ -237,6 +318,9 @@ class appmonitorcheck
      *         [params] => [array]            // optional; arguments for Check function
      *                                        // its keys depend on the function  
      *     ]
+     *     [group] => Group A
+     *     [parent] => field "name" of another check
+     *     [worstresult] => RESULT_WARNING
      * ]
      * </code>
      * @return array
@@ -244,8 +328,24 @@ class appmonitorcheck
     public function makeCheck(array $aConfig): array
     {
         $this->_iStart = microtime(true);
-        $this->_checkArrayKeys($aConfig, "name,description,check");
-        $this->_checkArrayKeys($aConfig["check"], "function");
+
+        $oVal=new validateparam();
+        $aErrors=$oVal->validateArray($this->_aCheckDocs, $aConfig);
+        if(count($aErrors)){
+            $this->_exit(
+                503,
+                __METHOD__ . " - validation of params failed\n"
+                    . "<pre>Errors: " 
+                        . print_r($aErrors, true)
+                        . "Input array was: " 
+                        . print_r($aConfig, true)
+                    . '</pre>',
+                22
+            );
+        }
+
+        $this->_checkArrayKeys($aConfig, "name,description,check", "group,parent,worstresult", true);
+        $this->_checkArrayKeys($aConfig["check"], "function", "params", true);
 
         $this->_aConfig = $aConfig;
         $this->_createDefaultMetadata();
@@ -264,33 +364,49 @@ class appmonitorcheck
         }
 
         if (!class_exists($sCheckClass)) {
-            header('HTTP/1.0 503 Service Unavailable');
-            die('<h1>503 Service Unavailable</h1>'
-                . '<h2>Details</h2>'
-                . __METHOD__ . " - check class not found: <code>$sCheckClass</code>"
-                . "<pre>" . print_r($aConfig, true) . '</pre>'
-                . "<h2>Known checks</h2>\n" . print_r($this->listChecks(), 1)
+            $this->_exit(
+                503,
+                __METHOD__ . " - [$aConfig[name]] - check class not found: <code>$sCheckClass</code>"
+                    . "<pre>" . print_r($aConfig, true) . '</pre>',
+                22
             );
         }
 
         $oPlugin = new $sCheckClass;
+        $aCheckDoc=$oPlugin->explain();
+
+        $aErrors=$oVal->validateArray($oPlugin->explain()['parameters']??[], $aParams);
+        if(count($aErrors)){
+            $this->_exit(
+                503,
+                __METHOD__ . " - [$aConfig[name]] - validation of check -> params failed\n"
+                    . "<pre>Errors: " 
+                        . print_r($aErrors, true)
+                        . "Input array: " 
+                        . print_r($aConfig, true)
+                    . '</pre>',
+                22
+            );
+        }
+            // die(__FILE__.":".__LINE__);
+
         $aResponse = $oPlugin->run($aParams);
         if (!is_array($aResponse)) {
-            header('HTTP/1.0 503 Service Unavailable');
-            die('<h1>503 Service Unavailable</h1>'
-                . '<h2>Details</h2>'
-                . __METHOD__ . " - plugin : $sCheck does not responses an array"
+            $this->_exit(
+                503,
+                __METHOD__ . " - plugin : $sCheck does not responses an array"
                 . "<pre>INPUT " . print_r($aConfig, true) . '</pre>'
-                . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>'
+                . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>',
+                23
             );
         }
         if (count($aResponse) < 2) {
-            header('HTTP/1.0 503 Service Unavailable');
-            die('<h1>503 Service Unavailable</h1>'
-                . '<h2>Details</h2>'
-                . __METHOD__ . " - plugin : $sCheck does not responses the minimum of 2 array values"
-                . "<pre>INPUT " . print_r($aConfig, true) . '</pre>'
-                . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>'
+            $this->_exit(
+                503,
+                __METHOD__ . " - plugin : $sCheck does not responses the minimum of 2 array values"
+                    . "<pre>INPUT " . print_r($aConfig, true) . '</pre>'
+                    . "<pre>RESPONSE " . print_r($aResponse, true) . '</pre>',
+                    24
             );
         }
         if (!isset($aResponse[2]) || !$aResponse[2]) {
@@ -315,6 +431,8 @@ class appmonitorcheck
     public function listChecks(): array
     {
         $aReturn = [];
+
+        // **DEPRECATED**
         // return internal protected fuctions named "check[whatever]"
         $class = new ReflectionClass($this);
         foreach ($class->getMethods(ReflectionMethod::IS_PROTECTED) as $oReflectionMethod) {
@@ -326,6 +444,15 @@ class appmonitorcheck
         foreach (glob($this->_sPluginDir . '/checks/*.php') as $sPluginFile) {
             $aReturn[str_replace('.php', '', basename($sPluginFile))] = 1;
         }
+
+        // from currently loaded classes
+        foreach(get_declared_classes() as $sClass){
+            if (strpos($sClass, "check") === 0) {
+                $aReturn[str_replace('check','',$sClass)] = 1;
+            }
+        }
+
+        array_unique($aReturn);
         ksort($aReturn);
         return array_keys($aReturn);
     }
@@ -406,17 +533,18 @@ class appmonitorcheck
         $power = 0;
         foreach ($this->_units as $sUnit) {
             if (preg_match('/^[0-9\.\ ]*' . $sUnit . '/', $sValue)) {
-                $i = preg_replace('/([0-9\.]*).*/', '$1', $sValue);
+                // $i = preg_replace('/([0-9\.]*).*/', '$1', $sValue);
+                $i = str_replace($sUnit, '', $sValue);
                 $iReal = $i * pow(1024, $power);
                 // die("FOUND: $sValue with unit ${sUnit} - 1024^$power * $i = $iReal");
                 return $iReal;
             }
             $power++;
         }
-        header('HTTP/1.0 503 Service Unavailable');
-        die('<h1>503 Service Unavailable</h1>'
-            . '<h2>Details</h2>'
-            . __METHOD__ . " ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units)
+        $this->_exit(
+            503,
+            __METHOD__ . " ERROR in space value parameter - there is no size unit in [$sValue] - allowed size units are " . implode('|', $this->_units),
+            25
         );
     }
 
diff --git a/public_html/appmonitor/classes/appmonitor-client.class.php b/public_html/appmonitor/classes/appmonitor-client.class.php
index 66def5a5d02705512751a93982d325823d7a35f3..ae2d3003900b206f50cf2187c23dee97bd6b90fa 100755
--- a/public_html/appmonitor/classes/appmonitor-client.class.php
+++ b/public_html/appmonitor/classes/appmonitor-client.class.php
@@ -44,8 +44,12 @@ if (!class_exists('appmonitorcheck')) {
  * 2024-07-19  0.137  axel.hahn@unibe.ch      php 8 only: use typed variables
  * 2024-11-22  0.141  axel.hahn@unibe.ch      Set client version to server version after updating http, mysqli and app checks
  * 2025-01-02  0.149  axel.hahn@unibe.ch      add getChecks method
+ * 2025-03-03  0.153  axel.hahn@unibe.ch      fix client checks during development of a compiled binary 
+ * 2025-03-04  0.154  axel.hahn@unibe.ch      finish with exitcode instead of die()
+ * 2025-03-17  0.155  axel.hahn@unibe.ch      added: getVersion() and setVersion()
+ * 2025-03-19  0.156  axel.hahn@unibe.ch      added: validation rules for parameters in all checks
  * --------------------------------------------------------------------------------<br>
- * @version 0.149
+ * @version 0.155
  * @author Axel Hahn
  * @link TODO
  * @license GPL
@@ -59,7 +63,7 @@ class appmonitor
      * Name and Version number
      * @var string
      */
-    protected string $_sVersion = 'php-client-v0.149';
+    protected string $_sVersion = '0.156';
 
     /**
      * config: default ttl for server before requesting the client check again
@@ -119,13 +123,14 @@ class appmonitor
             "ttl" => false,
             "result" => false,
             "time" => false,
-            "version" => $this->_sVersion,
+            "version" => false,
         ];
 
         // fill with default values
         $this->setHost();
         $this->setWebsite();
         $this->setTTL();
+        $this->setVersion();
         return true;
     }
 
@@ -151,28 +156,19 @@ class appmonitor
         $this->_aMeta["host"] = $s;
         return true;
     }
-
     /**
-     * Set a name for this website or application and its environment 
-     * (dev, test, prod); 
-     * 
-     * If you have several application in subdirectories, i.e. /blog,  /shop...
-     * then you should the path or any description to identify them too
-     * 
-     * if no argument is given the name of HTTP_HOST will be used
+     * Set final result in meta data; if no value was given then it
+     * sets the biggest value of any check.
      * 
-     * @param string $sWebsite  Name of the website or web application
+     * @param integer  $iResult  set resultcode; one of RESULT_OK|RESULT_WARNING|RESULT_ERROR|RESULT_UNKNOWN
      * @return boolean
      */
-    public function setWebsite($sWebsite = ''): bool
+    public function setResult(int $iResult = -1): bool
     {
-        if (!$sWebsite && isset($_SERVER["HTTP_HOST"])) {
-            $sWebsite = $_SERVER["HTTP_HOST"];
-        }
-        if (!$sWebsite) {
-            return false;
+        if ($iResult === -1) {
+            $iResult = $this->_iMaxResult; // see addCheck()
         }
-        $this->_aMeta["website"] = $sWebsite;
+        $this->_aMeta["result"] = $iResult;
         return true;
     }
 
@@ -192,18 +188,38 @@ class appmonitor
     }
 
     /**
-     * Set final result in meta data; if no value was given then it
-     * sets the biggest value of any check.
+     * Set a prefix for meta -> version ... before "-php-client-v<VERSION>"
+     * @param string $sVersionPrefix  new prefix
+     * @return bool
+     */
+    public function setVersion($sVersionPrefix = ""): bool
+    {
+        $this->_aMeta["version"] = ($sVersionPrefix ? "$sVersionPrefix-" : "")
+            . "php-client-v$this->_sVersion"
+        ;
+        return true;
+    }
+    /**
+     * Set a name for this website or application and its environment 
+     * (dev, test, prod); 
      * 
-     * @param integer  $iResult  set resultcode; one of RESULT_OK|RESULT_WARNING|RESULT_ERROR|RESULT_UNKNOWN
+     * If you have several application in subdirectories, i.e. /blog,  /shop...
+     * then you should the path or any description to identify them too
+     * 
+     * if no argument is given the name of HTTP_HOST will be used
+     * 
+     * @param string $sWebsite  Name of the website or web application
      * @return boolean
      */
-    public function setResult(int $iResult = -1): bool
+    public function setWebsite($sWebsite = ''): bool
     {
-        if ($iResult === -1) {
-            $iResult = $this->_iMaxResult; // see addCheck()
+        if (!$sWebsite && isset($_SERVER["HTTP_HOST"])) {
+            $sWebsite = $_SERVER["HTTP_HOST"];
         }
-        $this->_aMeta["result"] = $iResult;
+        if (!$sWebsite) {
+            return false;
+        }
+        $this->_aMeta["website"] = $sWebsite;
         return true;
     }
 
@@ -318,8 +334,11 @@ class appmonitor
                 return true;
             }
         }
-        header('HTTP/1.0 403 Forbidden');
-        die('ERROR: Your ip address [' . $sIP . '] has no access.');
+        $this->_exit(
+            403,
+            'ERROR: Your ip address [' . $sIP . '] has no access.',
+            11
+        );
     }
 
     /**
@@ -339,14 +358,26 @@ class appmonitor
         if (isset($_GET[$sVarname]) && $_GET[$sVarname] === $sToken) {
             return true;
         }
-        header('HTTP/1.0 403 Forbidden');
-        die('ERROR: A token is required.');
+        $this->_exit(
+            403,
+            'ERROR: A token is required.',
+            12
+        );
     }
 
     // ----------------------------------------------------------------------
     // getter
     // ----------------------------------------------------------------------
 
+    /**
+     * Get version of the appmoinitor php client
+     * @return string
+     */
+    public function getVersion(): string
+    {
+        return $this->_sVersion;
+    }
+
     /**
      * list all available check functions. This is a helper class you cann call
      * to get an overview over built in functions. You get a flat array with
@@ -381,9 +412,11 @@ class appmonitor
         }
 
         if (count($aErrors)) {
-            $this->abort(
-                '<h2>Error: client check is not complete</h2><p>Found errors:</p><ol><li>' . implode('<li>', $aErrors) . '</ol><br><br>'
-                // .'Dump of your data so far:<pre>' . json_encode($this->getResults(), JSON_PRETTY_PRINT) . '</pre><hr>'
+            $this->_exit(
+                503,
+                'Client check is not complete<br>Found errors:<br>'
+                . '<ol><li>' . implode('<li>', $aErrors) . '</ol><br><br>',
+                10
             );
         }
         return true;
@@ -394,14 +427,24 @@ class appmonitor
     // ----------------------------------------------------------------------
 
     /**
-     * Stop processing the client checks and abort with an error
-     * @param string $sMessage  text to show after a 503 headline
-     * @return void
+     * Exit execution with error message and given exicode.
+     * @param int    $iHttpcode  http statuscode
+     * @param string $sMessage   detailed message
+     * @param int    $iExitcode  exitcode
+     * @return never
      */
-    public function abort(string $sMessage): void
+    protected function _exit($iHttpcode, $sMessage, $iExitcode): never
     {
-        header('HTTP/1.0 503 Service Unavailable');
-        die('<h1>503 Service Unavailable</h1>' . $sMessage);
+
+        $aStatus = [
+            403 => 'Forbidden',
+            503 => 'Service Unavailable',
+        ];
+        header("HTTP/1.1 $iHttpcode $aStatus[$iHttpcode]");
+        echo "<h1>$iHttpcode $aStatus[$iHttpcode]</h1>
+            <h2>Details</h2>
+            $sMessage\n";
+        exit($iExitcode);
     }
 
     /**
@@ -433,7 +476,7 @@ class appmonitor
     {
         $this->_checkData();
         $this->_aMeta['time'] = number_format((microtime(true) - $this->_iStart) * 1000, 3) . 'ms';
-        $sOut=json_encode($this->getResults());
+        $sOut = json_encode($this->getResults());
 
         header('Content-type: application/json');
         header('Cache-Control: cache');
@@ -508,7 +551,7 @@ class appmonitor
             $sOut .= '<span class="result' . $i . '">' . $sText . '</span> ';
         }
 
-        $sRaw=json_encode($aData, JSON_PRETTY_PRINT);
+        $sRaw = json_encode($aData, JSON_PRETTY_PRINT);
         $sRaw = preg_replace('/:\ \"(.*)\"/U', ': "<span class="string">$1</span>"', $sRaw);
         $sRaw = preg_replace('/:\ ([0-9]*)/', ': <span class="int">$1</span>', $sRaw);
         $sRaw = preg_replace('/\"(.*)\":/U', '"<span class="key">$1</span>":', $sRaw);
diff --git a/public_html/appmonitor/classes/validateparam.class.php b/public_html/appmonitor/classes/validateparam.class.php
new file mode 100644
index 0000000000000000000000000000000000000000..dfdf41d4667b8aa2698d5ba07fa0620f9a84c0af
--- /dev/null
+++ b/public_html/appmonitor/classes/validateparam.class.php
@@ -0,0 +1,203 @@
+<?php
+class validateparam
+{
+
+    /**
+     * Summary of _aValidationDefs
+     * @var array
+     */
+    protected array $_aValidationDefs = [];
+
+    protected bool $_flag_debug = false;
+
+
+    /**
+     * Write debug line if _flag_debug is set to true
+     * @param string $s  message to show
+     * @return void
+     */
+    protected function _wd(string $s)
+    {
+        if ($this->_flag_debug) {
+            echo "DEBUG: $s<br>\n";
+        }
+    }
+
+    /**
+     * Include the default validation rules defined in validateparam.settings.php
+     */
+    public function __construct(){
+        // IMPORTANT:
+        // This style is not so nice like <variable> = include <file>;
+        // keep the include line unchanged - it is detected by installer 
+        // CLI client project (amcli)
+        include 'validateparam.settings.php';
+    }
+
+    /**
+     * shared validaten checks for floor and integer
+     * 
+     * @param array $aOpt  Validation rules
+     * @param mixed $value value to verify
+     * @return string with found error
+     */
+    protected function _checkCount(array $aOpt, mixed $value)
+    {
+        $sError = "";
+
+        if ($aOpt['min'] ?? false) {
+            if ($value < $aOpt['min']) {
+                $sError .= "Value is too small; minimum is $aOpt[min]. ";
+            }
+        }
+        if ($aOpt['max'] ?? false) {
+            if ($value > $aOpt['max']) {
+                $sError .= "Value is too big; maximum is $aOpt[max]. ";
+            }
+        }
+        if (isset($aOpt['oneof']) && is_array($aOpt['oneof'])) {
+            if (array_search($value, $aOpt['oneof']) == false) {
+                $sError .= "Value is invalid. Value doesn't match one of these values " . print_r($aOpt['oneof']);
+            }
+        }
+
+        return $sError;
+    }
+
+    /**
+     * Validate a single value. It returns a string with an errormessage.
+     * No error means ok.
+     * 
+     * @param array $aOpt  Validation rules
+     * @param mixed $value value to verify
+     * @return string with found error
+     */
+    public function validateValue(array $aOpt, mixed $value): string
+    {
+        $sError = '';
+
+        if (isset($aOpt['validate'])){
+
+            if ($this->_aValidationDefs[$aOpt['validate']] ?? false) {
+                $this->_wd("adding options ".print_r($this->_aValidationDefs[$aOpt['validate']],true));
+                $aOpt = array_merge($aOpt, $this->_aValidationDefs[$aOpt['validate']]);
+            } else {
+                $sError .= "Unknown value in 'validate'='$aOpt[validate]'";
+            }
+        }
+        // check type
+        if (isset($aOpt['type'])) {
+            $this->_wd("check type $aOpt[type]");
+            switch ($aOpt['type']) {
+                case 'array':
+                    if (!is_array($value)) {
+                        $sError .= "Value isn't an array";
+                    }
+                    break;
+                case 'bool':
+                    if (!is_bool($value)) {
+                        $sError .= "Value '$value' isn't a bool";
+                    }
+                    break;
+
+                case 'float':
+                    if (!is_float($value) && !is_int($value)) {
+                        $sError .= "Value isn't a float";
+                    } else {
+                        $sError .= $this->_checkCount($aOpt, $value);
+                    }
+                    break;
+
+                case 'int':
+                case 'integer':
+                    if (!is_int($value)) {
+                        $sError .= "Value '$value' isn't an integer";
+                    } else {
+                        $sError .= $this->_checkCount($aOpt, $value);
+                    }
+                    break;
+
+                case 'string':
+                    if (!is_string($value)) {
+                        $sError .= "Value '$value' isn't a string";
+                    } else {
+                        if ($aOpt['regex'] ?? false) {
+                            if (!preg_match($aOpt['regex'], $value)) {
+                                $sError .= "Value is invalid. Regex doesn't match: $aOpt[regex]";
+                            }
+                        }
+                        if (isset($aOpt['oneof']) && is_array($aOpt['oneof'])) {
+                            if (array_search($value, $aOpt['oneof']) == false) {
+                                $sError .= "Value is invalid. Value doesn't match one of these values " . print_r($aOpt['oneof']);
+                            }
+                        }
+                    }
+                    break;
+
+                default:
+                    $sError .= "ERROR Cannot validate unknown type: '$aOpt[type]'<br>\n";
+            }
+        }
+        // if string: verify regex
+
+        return $sError;
+    }
+
+    /**
+     * Validate an array of parameter definitions. It returns an array of all error messages
+     * No error / an empty array means ok.
+     * 
+     * @param mixed $aDefs    array with definitions
+     * @param mixed $aParams  array of given values
+     * @param mixed $bStrict  flag: strict checking to detect wrong keys.
+     * @return array with errors
+     */
+    public function validateArray(array $aDefs, array $aParams, bool $bStrict = true)
+    {
+        $aErrors = [];
+
+        // echo "<pre>";
+        // echo "aDefs = "; print_r($aDefs); 
+        // echo "aParams = "; print_r($aParams);
+        // echo "<hr>";
+        if (!count($aDefs)) {
+            return ['Defs' => 'No validation rules given.'];
+        }
+
+        $aTmp = $aParams;
+        foreach ($aDefs as $sKey => $aOpt) {
+            unset($aTmp[$sKey]);
+            if ($aOpt['required'] ?? false) {
+
+                $this->_wd("Check MUST $sKey");
+                // verify MUST field
+                if (!isset($aParams[$sKey])) {
+                    $aErrors[$sKey] = "Missing required key '$sKey'";
+                } else {
+                    $this->_wd("MUST field exists: $sKey");
+                    $sError = $this->validateValue($aOpt, $aParams[$sKey]);
+                    if ($sError) {
+                        $aErrors[$sKey] = $sError;
+                    } else {
+                        $this->_wd("$sKey was successfully validated.<hr>");
+                    }
+
+                }
+            }
+            if (isset($aParams[$sKey])) {
+                $this->_wd("Check OPTIONAL $sKey");
+                $sError = $this->validateValue($aOpt, $aParams[$sKey]);
+                if ($sError) {
+                    $aErrors[$sKey] = $sError;
+                }
+            }
+        }
+        if ($bStrict && isset($aTmp) && count($aTmp)) {
+            foreach (array_keys($aTmp) as $sKey) {
+                $aErrors[$sKey] = "Invalid key was found: '$sKey'; allowed keys are '" . implode("', '", array_keys($aDefs ?? [])) . "'";
+            }
+        }
+        $this->_wd("found errors: " . print_r($aErrors, 1));
+        return $aErrors;
+    }
+}
\ No newline at end of file
diff --git a/public_html/appmonitor/classes/validateparam.class.php.md b/public_html/appmonitor/classes/validateparam.class.php.md
new file mode 100644
index 0000000000000000000000000000000000000000..75e98b55ead259b33f96dff816c125dd34494d41
--- /dev/null
+++ b/public_html/appmonitor/classes/validateparam.class.php.md
@@ -0,0 +1,132 @@
+# validateparam.class.php
+
+## Description
+
+This class can validate values and arrays.
+You define type and validateion rules. A validation method checks the given value against your rules. It returns the error message(s). No error means, the value is ok.
+
+## Usage
+
+```php
+
+$oVal=new validateparam();
+
+// check an array
+$aErrors=$oVal->validateArray($aRules, $aGivenValues, $bStrict);
+if(count($aErrors)){
+    echo "Something is wrong: <pre>".print_r($aErrors, 1)."</pre>";
+    exit(1);
+}
+```
+
+## Validate Arrays
+
+First something visual - an example:
+
+```txt
+Array
+(
+    [name] => Array
+        (
+            [type] => string
+            [required] => true
+            [regex] => /./
+        )
+
+    [description] => Array
+        (
+            [type] => string
+            [required] => true
+            [regex] => /./
+        )
+        :
+    [worstresult] => Array
+        (
+            'type' => 'int',
+            'required' => false,
+            'description' => 'A failed check is max counted as given result. Use it on not required but optional checks',
+            'min'=>RESULT_OK,
+            'max'=>RESULT_ERROR,
+        )
+)
+```
+
+
+### Type check
+
+Each key can be marked to be a value of a given type.
+
+It can be a mandantory value or optional.
+
+| Name       | Type       | Description
+|--          |--          |--
+| 'type'     | {string}   | variable type that must match; one of "array", "bool", "count", "float", "int", "integer", "string"
+| 'required' | {bool}     | define value as required
+
+Next to these keys per type you can define validation rules in dependency of the type.
+
+### Validate numbers
+
+This section describes values of the `type`
+
+* int|integer - integer values
+* float - float values (or integer)
+
+Values you can verify if it is in a wanted range.
+
+| Name       | Type         | Description
+|--          |--            |--
+| 'min'      | {float\|int} | allowed minimum value
+| 'max'      | {float\|int} | allowed maximum value
+| 'oneof'    | {array}      | value must match one of the given values
+
+### Validate string
+
+Values of type "string" can be verified
+
+* against a given regex
+* with a set of allowed values
+
+| Name       | Type         | Description
+|--          |--            |--
+| 'regex'    | {string}     | value must match given regex
+| 'oneof'    | {array}      | value must match one of the given values
+
+### Validate with presets
+
+In the file *validateparam.settings.php* you can put presets and their validation rules for elements that repeat.
+
+```php
+<?php
+/*
+
+    validation rules 
+    if a param has the key 'validate' that matches a key then its values will
+    be added for validation.
+
+    SYNTAX
+
+    KEY - value for'validate' key
+    VALUE - array with these possible keys
+            - type  - set a type
+            - regex - define a regex for type sting
+            - min, max - range for types float and int
+            - oneof
+*/
+
+
+return [
+    'hostname'   => [ 'type' => 'string', 'regex' => '/^[a-z0-9\_\-\.]/i'],
+    'portnumber' => [ 'type' => 'int',    'min' => 0, 'max' => 65535],
+    'website'    => [ 'type' => 'string', 'regex' => '/https?:\/\//'],
+];
+```
+
+```txt
+[port] => Array
+    (
+        [type] => int
+        [required] => true
+        [validate] => portnumber
+    )
+```
diff --git a/public_html/appmonitor/classes/validateparam.settings.php b/public_html/appmonitor/classes/validateparam.settings.php
new file mode 100644
index 0000000000000000000000000000000000000000..fdc84648519e84e85dd6cc12d87272a734d72b59
--- /dev/null
+++ b/public_html/appmonitor/classes/validateparam.settings.php
@@ -0,0 +1,25 @@
+<?php
+/*
+
+    validation rules 
+    if a param has the key 'validate' that matches a key then its values will
+    be added for validation.
+
+    This file is included in the constructor of the validateparam class
+
+    SYNTAX:
+
+    KEY - value for'validate' key
+    VALUE - array with these possible keys
+            - type  - set a type
+            - regex - define a regex for type sting
+            - min, max - range for types float and int
+            - oneof
+*/
+
+
+$this->_aValidationDefs = [
+    'hostname'   => [ 'type' => 'string', 'regex' => '/^[a-z0-9\_\-\.]/i'],
+    'portnumber' => [ 'type' => 'int',    'min' => 0, 'max' => 65535],
+    'website'    => [ 'type' => 'string', 'regex' => '/https?:\/\//'],
+];
diff --git a/public_html/appmonitor/classes/validateparam.settings.php.dist b/public_html/appmonitor/classes/validateparam.settings.php.dist
new file mode 100644
index 0000000000000000000000000000000000000000..980f6bf9c400985b2e8b046946e8b3a2c4409e9c
--- /dev/null
+++ b/public_html/appmonitor/classes/validateparam.settings.php.dist
@@ -0,0 +1,23 @@
+<?php
+/*
+
+    validation rules 
+    if a param has the key 'validate' that matches a key then its values will
+    be added for validation.
+
+    SYNTAX
+
+    KEY - value for'validate' key
+    VALUE - array with these possible keys
+            - type  - set a type
+            - regex - define a regex for type sting
+            - min, max - range for types float and int
+            - oneof
+*/
+
+
+$this->_aValidationDefs = [
+    'hostname'   => [ 'type' => 'string', 'regex' => '/^[a-z0-9\_\-\.]/i'],
+    'portnumber' => [ 'type' => 'int',    'min' => 0, 'max' => 65535],
+    'website'    => [ 'type' => 'string', 'regex' => '/https?:\/\//'],
+];
diff --git a/public_html/appmonitor/index.php b/public_html/appmonitor/index.php
index f109b8a4e0a52d22ae127180fe99ef494b8225cd..1afda2a2fc8e0d40cb271de9fad785ffd4b9b754 100644
--- a/public_html/appmonitor/index.php
+++ b/public_html/appmonitor/index.php
@@ -362,7 +362,7 @@ $oMonitor->addCheck(
     [
         "name" => "plugin Load",
         "description" => "current load",
-        "parent" => false,
+        "parent" => null,
         "check" => [
             "function" => "Loadmeter",
             "params" => [
@@ -377,7 +377,7 @@ $oMonitor->addCheck(
     [
         "name" => "plugin ApacheProcesses",
         "description" => "Apache processes",
-        "parent" => false,
+        "parent" => null,
         "check" => [
             "function" => "ApacheProcesses",
             "params" => [
diff --git a/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php b/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php
index 87473021d5cbe8b47d9ac9de7b512f732e593636..83618b00f69273937a5b29214526d34c62457fe2 100755
--- a/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php
+++ b/public_html/appmonitor/plugins/apps/iml-appmonitor-server.php
@@ -14,6 +14,7 @@
  * 2022-03-28  move checks into plugins/apps/
  * 2024-07-23  php 8: short array syntax
  * 2024-12-28  added check for custom config and url file (if they exist)
+ * 2025-02-24  add checks for sqlite and data dir
  */
 
 // ----------------------------------------------------------------------
@@ -28,7 +29,7 @@ $oMonitor->addCheck(
         "check" => [
             "function" => "Phpmodules",
             "params" => [
-                "required" => ["curl"],
+                "required" => ["curl", "json", "pdo_sqlite"],
                 "optional" => [],
             ],
         ],
@@ -123,6 +124,22 @@ if (is_file("$sApproot/server/config/appmonitor-server-urls.json")) {
     );        
 }
 
+$oMonitor->addCheck(
+    [
+        "name" => "write to ./data/",
+        "description" => "Check sqlite data directory",
+        // "group" => "folder",
+        "check" => [
+            "function" => "File",
+            "params" => [
+                "filename" => "$sApproot/server/data",
+                "dir" => true,
+                "writable" => true,
+            ],
+        ],
+    ]
+);
+
 // ----------------------------------------------------------------------
 // protect dirs against web access
 // specialty: if the test results in an error, the total result switches
@@ -132,7 +149,7 @@ $sBaseUrl = 'http' . (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] ? 's' : '')
     . '://' . $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT']
     . dirname(dirname($_SERVER['REQUEST_URI']));
 
-foreach (['server/config', 'server/tmp'] as $sMyDir) {
+foreach (['server/config', 'server/data', 'server/tmp'] as $sMyDir) {
     $oMonitor->addCheck(
         [
             "name" => "http to $sMyDir",
@@ -154,14 +171,58 @@ foreach (['server/config', 'server/tmp'] as $sMyDir) {
 // count of current projects
 // ----------------------------------------------------------------------
 require_once($sApproot . '/server/classes/appmonitor-server.class.php');
-$oServer = new appmonitorserver();
+$oServer = new appmonitorserver(false);
+
+$aCfg=$oServer->getConfigVars();
+
+$oMonitor->addCheck(
+    [
+        "name" => "db-config",
+        "description" => "Check if the service is running",
+        // "group" => "service",
+        "parent" => "check custom config file",
+        "check" => [
+            "function" => "Simple",
+            "params" => [
+                "result" => ($aCfg['db']['dsn'] ? RESULT_OK : RESULT_ERROR),
+                "value" => ($aCfg['db']['dsn']
+                    ? "OK: dsn was set"
+                    :"Error: database dsn was not found in config."
+                )
+            ],
+        ]
+    ]
+);
+
+if($aCfg['db']['dsn']){
+
+    $aDB=[
+        'connect'=>$aCfg['db']['dsn'] ?? '',
+        'user'=>$aCfg['db']['user'] ?? '',
+        'password'=> $aCfg['db']['password'] ?? ''
+    ];
+    // print_r($aDB);
+    $oMonitor->addCheck(
+        [
+            "name" => "connect db",
+            "description" => "Connnect PDO database",
+            "group" => "database",
+            "parent" => "db-config",
+            "check" => [
+                "function" => "PdoConnect",
+                "params" => $aDB
+            ],
+        ]
+    );
+} 
+
 $iCount = count($oServer->getAppIds());
 $oMonitor->addCheck(
     [
         "name" => "appcounter",
         "description" => "Monitored apps",
         "group" => "monitor",
-        "parent" => false,
+        "parent" => null,
         "check" => [
             "function" => "Simple",
             "params" => [
@@ -173,6 +234,7 @@ $oMonitor->addCheck(
         ],
     ]
 );
+
 // ----------------------------------------------------------------------
 // check running service
 // ----------------------------------------------------------------------
@@ -211,7 +273,7 @@ $oMonitor->addCheck(
         "name" => "plugin Load",
         "description" => "current load",
         "group" => 'monitor',
-        "parent" => false,
+        "parent" => null,
         "check" => [
             "function" => "Loadmeter",
             "params" => [
diff --git a/public_html/appmonitor/plugins/apps/wordpress.php b/public_html/appmonitor/plugins/apps/wordpress.php
index 1ac052034d32a09d01b14fc6dae2e730f79c2160..49f3d00552c646ade912815bab0be896ff4968ca 100644
--- a/public_html/appmonitor/plugins/apps/wordpress.php
+++ b/public_html/appmonitor/plugins/apps/wordpress.php
@@ -64,8 +64,10 @@ $aDb = [
 // checks
 // ----------------------------------------------------------------------
 
-// required php modules
-// see https://ertano.com/required-php-modules-for-wordpress/
+// required php modules * WIP
+// see https://wordpress.org/about/requirements/ << doesn't say anything about php modules
+// see https://ertano.com/rired-php-modules-for-wordpress/ << too many modules
+// see https://zeropointdevelopment.com/required-php-extensions-for-wordpress-wpquickies/
 $oMonitor->addCheck(
     [
         "name" => "PHP modules",
@@ -76,22 +78,24 @@ $oMonitor->addCheck(
             "params" => [
                 "required" => [
                     // "cmath",
-                    "cli",
+                    // "cli",
                     "curl",
                     "date",
                     "dom",
                     "fileinfo",
-                    "filter",
-                    "gd",
+                    // "filter",
+                    // "gd",
                     "gettext",
                     "hash",
-                    "iconv",
+                    // "iconv",
                     "imagick",
                     "json",
                     // "libsodium",
-                    "mysql",
+                    "mbstring",
+                    "mysqli",
                     "openssl",
                     "pcre",
+                    "sodium",
                     // "opcache",
                     // "readline",
                     "xml",
diff --git a/public_html/appmonitor/plugins/checks/apacheprocesses.php b/public_html/appmonitor/plugins/checks/apacheprocesses.php
index 400c880bf9d2724e6adda5caa3df45d156aa12e7..262ffdb227422125bc151c12b40e0e9a472d9b53 100755
--- a/public_html/appmonitor/plugins/checks/apacheprocesses.php
+++ b/public_html/appmonitor/plugins/checks/apacheprocesses.php
@@ -45,7 +45,8 @@
  * 2019-06-07  <axel.hahn@iml.unibe.ch>
  * 2022-07-06  <axel.hahn@iml.unibe.ch>  set group "monitor"
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
- * 
+ * 2025-03-04  <axel.hahn@unibe.ch>      fix sComment error on failed apache status
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkApacheProcesses extends appmonitorcheck
 {
@@ -69,39 +70,37 @@ class checkApacheProcesses extends appmonitorcheck
     protected float $_iError = 75;
 
     /**
-     * Self documentation (as idea)
-     * @return array
+     * Self documentation and validation rules
+     * @var array
      */
-    public function explain(): array
-    {
-        return [
-            'name' => 'Plugin ApacheProcesses',
-            'descriptionm' => 'Check count running Apache processes',
-            'parameters' => [
-                'url' => [
-                    'type' => 'string',
-                    'required' => false,
-                    'decsription' => 'Override https server-status page; default is http://localhost/server-status; Use it if the protocol to localhost is not http, but https or if it requires an authentication',
-                    'default' => $this->_sServerStatusUrl,
-                    'example' => '',
-                ],
-                'warning' => [
-                    'type' => 'float',
-                    'required' => false,
-                    'decsription' => 'Limit to switch to warning (in percent)',
-                    'default' => $this->_iWarn,
-                    'example' => 30,
-                ],
-                'error' => [
-                    'type' => 'float',
-                    'required' => false,
-                    'decsription' => 'Limit to switch to critical (in percent)',
-                    'default' => $this->_iError,
-                    'example' => 50,
-                ],
+    protected array $_aDoc = [
+        'name' => 'Plugin ApacheProcesses',
+        'description' => 'Check count running Apache processes',
+        'parameters' => [
+            'url' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Override https server-status page; default is http://localhost/server-status; Use it if the protocol to localhost is not http, but https or if it requires an authentication',
+                'default' => "http://localhost/server-status",
+                'regex'=>'/^https?:\/\/[^\s]+/',
+                'example' => '',
             ],
-        ];
-    }
+            'warning' => [
+                'type' => 'float',
+                'required' => false,
+                'description' => 'Limit to switch to warning (in percent)',
+                'default' => "50",
+                'example' => 30,
+            ],
+            'error' => [
+                'type' => 'float',
+                'required' => false,
+                'description' => 'Limit to switch to critical (in percent)',
+                'default' => "75",
+                'example' => 50,
+            ],
+        ],
+    ];
 
     /**
      * Fetch http server status and return slots, active and waiting processes
@@ -112,7 +111,7 @@ class checkApacheProcesses extends appmonitorcheck
      */
     protected function _getApacheProcesses(): bool|array
     {
-        $sBody = file_get_contents($this->_sServerStatusUrl);
+        $sBody = @file_get_contents($this->_sServerStatusUrl);
         if (!$sBody) {
             return false;
         }
@@ -163,12 +162,12 @@ class checkApacheProcesses extends appmonitorcheck
         // --- (2) do something magic
         $aProcesses = $this->_getApacheProcesses();
         $iActive = $aProcesses ? $aProcesses['active'] : false;
+        $sComment = '';
 
         // set result code
         if ($iActive === false) {
             $iResult = RESULT_UNKNOWN;
         } else {
-            $sComment = '';
             $iTotal = $aProcesses['total'];
             $iResult = RESULT_OK;
             if (($iActive / $iTotal * 100) > $this->_iWarn) {
diff --git a/public_html/appmonitor/plugins/checks/cert.php b/public_html/appmonitor/plugins/checks/cert.php
index a6705b23df2429cbeca41d00d63add35432eb81a..311e6d58c965a66fa4552ee365f9602e119bb6f7 100755
--- a/public_html/appmonitor/plugins/checks/cert.php
+++ b/public_html/appmonitor/plugins/checks/cert.php
@@ -40,10 +40,52 @@
  * 2022-05-02  <axel.hahn@iml.unibe.ch>  set warning to 21 days (old value was 30); add "critical" param
  * 2022-05-03  <axel.hahn@iml.unibe.ch>  critical limit is a warning only (because app is still functional)
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
- * 
+ * 2025-03-03  <axel.hahn@unibe.ch>      comment block for host check in DND names
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkCert extends appmonitorcheck
 {
+
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin Cert',
+        'description' => 'Check if a SSL certificate is still valid … and does not expire soon.',
+        'parameters' => [
+            'url' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => 'Url to check https://[server}[:{port}] or ssl://[server}[:{port}]',
+                'default' => null,
+                'regex'=>'/^(https|ssl):\/\/[^\s]+/',
+                'example' => '',
+            ],
+            'verify' => [
+                'type' => 'bool',
+                'required' => false,
+                'description' => 'optional: flag verify certificate; default = true',
+                'default' => true,
+                'example' => "false",
+            ],
+            'warning' => [
+                'type' => 'int',
+                'required' => false,
+                'description' => 'optional: count of days to warn; default=21',
+                'default' => 21,
+                'example' => 30,
+            ],
+            'critical' => [
+                'type' => 'int',
+                'required' => false,
+                'description' => 'optional: count of days to raise critical; default=5',
+                'default' => 5,
+                'example' => "7",
+            ],
+        ],
+    ];
+
     /**
      * Get default group of this check
      * @return string
@@ -80,6 +122,11 @@ class checkCert extends appmonitorcheck
             ];
         }
 
+        /*
+        
+            unneeded:
+            when verify is true (=default) then it cannot connect with wrong certificate
+        
         $sDNS = $certinfo['extensions']['subjectAltName'] ?? false;
         $sHost = parse_url($sUrl, PHP_URL_HOST);
         if (strstr($sDNS, "DNS:$sHost") === false) {
@@ -88,6 +135,7 @@ class checkCert extends appmonitorcheck
                 "Wrong certificate: $sHost is not listed as DNS alias in [$sDNS]. $sMessage"
             ];
         }
+        */
 
         $iDaysleft = round(($certinfo['validTo_time_t'] - date('U')) / 60 / 60 / 24);
         $sMessage .= 'Issuer: ' . $certinfo['issuer']['O']
diff --git a/public_html/appmonitor/plugins/checks/diskfree.php b/public_html/appmonitor/plugins/checks/diskfree.php
index 86e8efc8d4f74547b9e1647074167e59cb3af206..78c14fe093d9bd578abb7bb333daef7751674f35 100755
--- a/public_html/appmonitor/plugins/checks/diskfree.php
+++ b/public_html/appmonitor/plugins/checks/diskfree.php
@@ -20,9 +20,44 @@
  * 2021-10-26  <axel.hahn@iml.unibe.ch>
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 2025-01-02  <www.axel-hahn.de>        update output
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkDiskfree extends appmonitorcheck
 {
+
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin Diskfree',
+        'description' => 'Check if a given filesystem / directory that it has enough space.',
+        'parameters' => [
+            'directory' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => 'directory to check',
+                'default' => null,
+                'regex'=>'/./',
+                'example' => '',
+            ],
+            'warning' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'size for warning level',
+                'default' => 21,
+                'example' => "1.25GB",
+            ],
+            'critical' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => 'size for critical level',
+                'default' => 5,
+                'example' => "500.7MB",
+            ],
+        ],
+    ];
+
     /**
      * Get default group of this check
      * @return string
diff --git a/public_html/appmonitor/plugins/checks/exec.php b/public_html/appmonitor/plugins/checks/exec.php
index 6221ca6fed432c724e4339e6b030d7cc73fd3e14..6731d2e9a220a30df51384836618d1761e58445d 100644
--- a/public_html/appmonitor/plugins/checks/exec.php
+++ b/public_html/appmonitor/plugins/checks/exec.php
@@ -21,10 +21,58 @@
  * 
  * 2022-09-19  <axel.hahn@iml.unibe.ch>
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
- * 
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkExec extends appmonitorcheck
 {
+
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin Exec',
+        'description' => 'Execute a shell command.',
+        'parameters' => [
+            'command' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => 'Command line',
+                'default' => null,
+                'regex'=>'/./',
+                'example' => '[command] [parameters] 2>&1',
+            ],
+            'output' => [
+                'type' => 'bool',
+                'required' => false,
+                'description' => 'size for warning level',
+                'default' => 21,
+                'example' => "1.25GB",
+            ],
+            'exitOk' => [
+                'type' => 'array',
+                'required' => false,
+                'description' => 'array of integers for ok exitcodes',
+                'default' => null,
+                'example' => "",
+            ],
+            'exitWarn' => [
+                'type' => 'array',
+                'required' => false,
+                'description' => 'array of integers for warning exitcodes',
+                'default' => null,
+                'example' => "",
+            ],
+            'exitCritical' => [
+                'type' => 'array',
+                'required' => false,
+                'description' => 'array of integers for critical exitcodes',
+                'default' => null,
+                'example' => "",
+            ],
+        ],
+    ];
+
     /**
      * Get default group of this check
      * @return string
diff --git a/public_html/appmonitor/plugins/checks/file.php b/public_html/appmonitor/plugins/checks/file.php
index f0aafde19c2330ced2a1d19762913db36e625b74..c4bc8713176053322c50cf0dc342a16fe2d08797 100755
--- a/public_html/appmonitor/plugins/checks/file.php
+++ b/public_html/appmonitor/plugins/checks/file.php
@@ -22,9 +22,79 @@
  * 
  * 2021-10-26  <axel.hahn@iml.unibe.ch>
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
+ * 2025-03-01  <axel.hahn@unibe.ch>      fix check for files that must be absent
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkFile extends appmonitorcheck
 {
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin File',
+        'description' => 'Check existing or absent file objects: files, directories, softlinks',
+        'parameters' => [
+            'filename' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => 'filename or directory to check',
+                'default' => "",
+                'regex' => '/./',
+                'example' => '/var/www/myfile.txt',
+            ],
+            'exists' => [
+                'type' => 'bool',
+                'required' => false,
+                'description' => 'true = file object must exist (default); false = file object must not exist',
+                'default' => true,
+                'example' => "true",
+            ],
+            'dir' => [
+                'type' => 'bool',
+                'required' => false,
+                'description' => 'If true then check if fileobject is a directory',
+                'default' => null,
+                'example' => "false",
+            ],
+            'file' => [
+                'type' => 'bool',
+                'required' => false,
+                'description' => 'If true then check if fileobject is a file',
+                'default' => null,
+                'example' => "true",
+            ],
+            'link' => [
+                'type' => 'bool',
+                'required' => false,
+                'description' => 'If true then check if fileobject is a file',
+                'default' => null,
+                'example' => "true",
+            ],
+            'executable' => [
+                'type' => 'bool',
+                'required' => false,
+                'description' => 'If true then check if fileobject has executable permissions; if false it must be not executable',
+                'default' => null,
+                'example' => "false",
+            ],
+            'readable' => [
+                'type' => 'bool',
+                'required' => false,
+                'description' => 'If true then check if fileobject is readable; if false it must be not readable',
+                'default' => null,
+                'example' => "true",
+            ],
+            'writable' => [
+                'type' => 'bool',
+                'required' => false,
+                'description' => 'If true then check if fileobject is writable; ; if false it must be not writable',
+                'default' => null,
+                'example' => "true",
+            ],
+        ],
+    ];
+
     /**
      * Get default group of this check
      * @param array   $aParams - see run() method
@@ -68,7 +138,10 @@ class checkFile extends appmonitorcheck
 
         if (isset($aParams['exists'])) {
             $sMyflag = 'exists=' . ($aParams['exists'] ? 'yes' : 'no');
-            if (file_exists($sFile) && $aParams['exists']) {
+            if (
+                file_exists($sFile) && $aParams['exists']
+                || !file_exists($sFile) && !$aParams['exists']
+            ) {
                 $aOK[] = $sMyflag;
             } else {
                 $aErrors[] = $sMyflag;
diff --git a/public_html/appmonitor/plugins/checks/hello.php b/public_html/appmonitor/plugins/checks/hello.php
index e2ba5e60f28924b57535c5f5da9c68ee8fed8354..3f5c516ac1112ecbfea2bf2b3402ae9dabf1b7fa 100755
--- a/public_html/appmonitor/plugins/checks/hello.php
+++ b/public_html/appmonitor/plugins/checks/hello.php
@@ -41,10 +41,28 @@
  * 
  * 2019-06-05  <axel.hahn@iml.unibe.ch>
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
- * 
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkHello extends appmonitorcheck
 {
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin Hello',
+        'description' => 'Show a simple message',
+        'parameters' => [
+            'message' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => 'Message to show',
+                'default' => "",
+                'regex' => '/./',
+                'example' => 'Here I am',
+            ],
+        ],
+    ];
 
     /**
      * Run the check
diff --git a/public_html/appmonitor/plugins/checks/httpcontent.php b/public_html/appmonitor/plugins/checks/httpcontent.php
index 3d981c54e7a34dcf29f9473681d7e2a2be8e950c..c05e47f8785d29246eb4b32d8759fac19ef9be0a 100755
--- a/public_html/appmonitor/plugins/checks/httpcontent.php
+++ b/public_html/appmonitor/plugins/checks/httpcontent.php
@@ -22,9 +22,115 @@
  * 2023-07-06  <axel.hahn@unibe.ch>      add flag userpwd
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 2024-11-22  <axel.hahn@unibe.ch>      Return unknown if curl module is not active
+ * 2025-03-17  <axel.hahn@unibe.ch>      Fix check for http status code
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkHttpContent extends appmonitorcheck
 {
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin HttpContent',
+        'description' => 'This check verifies if a given url can be requested. Optionally you can test if it follows wanted rules.',
+        'parameters' => [
+            'url' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => 'Url to fetch',
+                'default' => "",
+                'regex' => '/https?:\/\//',
+                'example' => 'https://www.example.com/',
+            ],
+            'userpwd' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'User and password; syntax: “[username]:[password]”',
+                'regex' => '/.*:.*/',
+                'default' => "",
+                'example' => "myuser:aVerySecretPassword",
+            ],
+            'timeout' => [
+                'type' => 'int',
+                'required' => false,
+                'description' => 'Timeout in sec',
+                'default' => 5,
+                'example' => "10",
+            ],
+            'headeronly' => [
+                'type' => 'bool',
+                'required' => false,
+                'description' => 'flag to fetch http response herader only (HEAD request); default: false = returns header and body;',
+                'default' => false,
+                'example' => "true",
+            ],
+            'follow' => [
+                'type' => 'bool',
+                'required' => false,
+                'description' => 'flag to follow a location; default: false = do not follow; If you set it to true it ries to follow (but this is not a safe method)',
+                'default' => false,
+                'example' => "true",
+            ],
+            'sslverify' => [
+                'type' => 'bool',
+                'required' => false,
+                'description' => 'Enable/ disable verification of ssl certificate; default: true (verification is on)',
+                'default' => true,
+                'example' => "false",
+            ],
+            'status' => [
+                'type' => 'int',
+                'required' => false,
+                'description' => 'Test for an expected http status code; if none is given then test fails on status 400 and greater.',
+                'default' => null,
+                'example' => "401",
+            ],
+            'headercontains' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Test for a string in the http response header; it returns OK if the text was found',
+                'default' => null,
+                'example' => "Content-Type: text/css",
+            ],
+            'headernotcontains' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Test for a string in the http response header; it returns OK if the text was not found',
+                'default' => null,
+                'example' => "Server:",
+            ],
+            'headerregex' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Test for a regex in the http response header; it returns OK if the regex matches',
+                'default' => null,
+                'example' => "",
+            ],
+            'bodycontains' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Test for a string in the http response body; it returns OK if the text was found',
+                'default' => null,
+                'example' => "Content-Type: text/css",
+            ],
+            'bodynotcontains' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Test for a string in the http response body; it returns OK if the text was not found',
+                'default' => null,
+                'example' => "Server:",
+            ],
+            'bodyregex' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Test for a regex in the http response body; it returns OK if the regex matches',
+                'default' => null,
+                'example' => "",
+            ],
+        ],
+    ];
+
     /**
      * Get default group of this check
      * It is a "service" icon or "deny" for expected failures
@@ -32,7 +138,7 @@ class checkHttpContent extends appmonitorcheck
      * @param array   $aParams with optional 'status' containing http response code
      * @return string
      */
-    public function getGroup(array $aParams=[]): string
+    public function getGroup(array $aParams = []): string
     {
         $sReturn = 'service';
         if (isset($aParams['status']) && $aParams['status'] > 300 && $aParams['status'] < 500) {
@@ -67,7 +173,7 @@ class checkHttpContent extends appmonitorcheck
     {
         $this->_checkArrayKeys($aParams, "url");
         if (!function_exists("curl_init")) {
-            return [RESULT_UNKNOWN, "UNKNOWN: Unable to perform mysqli test. The php-curl module is not active."];
+            return [RESULT_UNKNOWN, "UNKNOWN: Unable to perform http test. The php-curl module is not active."];
         }
         $bShowContent = (isset($aParams["content"]) && $aParams["content"]) ? true : false;
         $ch = curl_init($aParams["url"]);
@@ -141,12 +247,12 @@ class checkHttpContent extends appmonitorcheck
         // ---------- check functions
 
         // --- http status code
-        $sOut .= "Http status: " . $aInfos['http_code'] . " - ";
+        $sOut .= "Http status: $aInfos[http_code] - ";
         if (isset($aParams["status"])) {
-            if ($aInfos['http_code'] === $aParams["status"]) {
-                $sOut .= "compare OK<br>";
+            if ($aInfos['http_code'] == $aParams["status"]) {
+                $sOut .= "as expected - OK<br>";
             } else {
-                $sOut .= "compare failed<br>";
+                $sOut .= "compare failed - not eaqual $aParams[status]<br>";
                 $bError = true;
             }
         } else {
@@ -159,7 +265,7 @@ class checkHttpContent extends appmonitorcheck
         }
         // --- http header
         if (isset($aParams["headercontains"]) && $aParams["headercontains"]) {
-            $sOut .= "Http header contains &quot;" . $aParams["headercontains"] . "&quot; - ";
+            $sOut .= "Http header contains '$aParams[headercontains]' - ";
             if (!strstr($sHttpHeader, $aParams["headercontains"]) === false) {
                 $sOut .= "compare OK<br>";
             } else {
@@ -168,7 +274,7 @@ class checkHttpContent extends appmonitorcheck
             }
         }
         if (isset($aParams["headernotcontains"]) && $aParams["headernotcontains"]) {
-            $sOut .= "Http header does not contain &quot;" . $aParams["headernotcontains"] . "&quot; - ";
+            $sOut .= "Http header does not contain '$aParams[headernotcontains]' - ";
             if (strstr($sHttpHeader, $aParams["headernotcontains"]) === false) {
                 $sOut .= "compare OK<br>";
             } else {
@@ -177,7 +283,7 @@ class checkHttpContent extends appmonitorcheck
             }
         }
         if (isset($aParams["headerregex"]) && $aParams["headerregex"]) {
-            $sOut .= "Http header regex test &quot;" . $aParams["headerregex"] . "&quot; - ";
+            $sOut .= "Http header regex test '$aParams[headerregex]' - ";
             try {
                 $bRegex = preg_match($aParams["headerregex"], $sHttpHeader);
                 if ($bRegex) {
@@ -193,7 +299,7 @@ class checkHttpContent extends appmonitorcheck
         }
         // --- http body
         if (isset($aParams["bodycontains"]) && $aParams["bodycontains"]) {
-            $sOut .= "Http body contains &quot;" . $aParams["bodycontains"] . "&quot; - ";
+            $sOut .= "Http body contains '$aParams[bodycontains]' - ";
             if (!strstr($sHttpBody, $aParams["bodycontains"]) === false) {
                 $sOut .= "compare OK<br>";
             } else {
@@ -202,7 +308,7 @@ class checkHttpContent extends appmonitorcheck
             }
         }
         if (isset($aParams["bodynotcontains"]) && $aParams["bodynotcontains"]) {
-            $sOut .= "Http body does not contain &quot;" . $aParams["bodynotcontains"] . "&quot; - ";
+            $sOut .= "Http body does not contain '$aParams[bodynotcontains]' - ";
             if (strstr($sHttpBody, $aParams["bodynotcontains"]) === false) {
                 $sOut .= "compare OK<br>";
             } else {
@@ -211,7 +317,7 @@ class checkHttpContent extends appmonitorcheck
             }
         }
         if (isset($aParams["bodyregex"]) && $aParams["bodyregex"]) {
-            $sOut .= "Http body regex test &quot;" . $aParams["bodyregex"] . "&quot; - ";
+            $sOut .= "Http body regex test '$aParams[bodyregex]' - ";
             try {
                 $bRegex = preg_match($aParams["bodyregex"], $sHttpBody);
                 if ($bRegex) {
@@ -229,12 +335,12 @@ class checkHttpContent extends appmonitorcheck
         if (!$bError) {
             return [
                 RESULT_OK,
-                'OK: http check "' . $aParams["url"] . '".<br>' . $sOut
+                "OK: http check '$aParams[url]'<br>$sOut"
             ];
         } else {
             return [
                 RESULT_ERROR,
-                'ERROR: http check "' . $aParams["url"] . '".<br>' . $sOut
+                "ERROR: http check '$aParams[url]'<br>$sOut"
             ];
         }
 
diff --git a/public_html/appmonitor/plugins/checks/loadmeter.php b/public_html/appmonitor/plugins/checks/loadmeter.php
index 13911af15f71926e01f23cce97154217fe076ccd..00eaacfa4312a19c05c0a90cd9c04bf417eb2630 100755
--- a/public_html/appmonitor/plugins/checks/loadmeter.php
+++ b/public_html/appmonitor/plugins/checks/loadmeter.php
@@ -45,10 +45,37 @@
  * 2019-06-06  <axel.hahn@iml.unibe.ch>
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 2024-07-25  <axel.hahn@unibe.ch>      float return with 2 digits behind comma
- * 
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkLoadmeter extends appmonitorcheck
 {
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin Loadmeter',
+        'description' => 'Get system load and render it as a tile.',
+        'parameters' => [
+            'warning' => [
+                'type' => 'float',
+                'required' => false,
+                'description' => 'Warning level',
+
+                // doc
+                'default' => null,
+                'example' => '1.5',
+            ],
+            'error' => [
+                'type' => 'float',
+                'required' => false,
+                'description' => 'Critical level when to raise an error',
+                'default' => null,
+                'example' => '2.5',
+            ],
+        ],
+    ];
+
     /**
      * Get default group of this check
      * @return string
diff --git a/public_html/appmonitor/plugins/checks/mysqlconnect.php b/public_html/appmonitor/plugins/checks/mysqlconnect.php
index 5fe8a611cccb570bdd57475a915b11a9f16c96eb..48940afd92acd10c92a1143bb45a2457501d6cf5 100755
--- a/public_html/appmonitor/plugins/checks/mysqlconnect.php
+++ b/public_html/appmonitor/plugins/checks/mysqlconnect.php
@@ -20,9 +20,70 @@
  * 2021-10-27  <axel.hahn@iml.unibe.ch>
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
  * 2024-11-22  <axel.hahn@unibe.ch>      detect installed mysqli function
+ * 2025-03-01  <axel.hahn@unibe.ch>      add try catch 
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkMysqlConnect extends appmonitorcheck
 {
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin Mysqlconnect',
+        'description' => 'Verify a database connection with mysqli real connect function.',
+        'parameters' => [
+            'server' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => 'Hostname/ ip of mysql server',
+                'validate' => 'hostname',
+
+                // doc
+                'default' => null,
+                'example' => 'localhost',
+            ],
+            'port' => [
+                'type' => 'int',
+                'required' => false,
+                'description' => 'port of mysql server',
+                'validate' => 'portnumber',
+
+                'default' => 5,
+                'example' => '3',
+            ],
+            'user' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Database user to connect with',
+                'default' => null,
+                'example' => 'dbuser',
+            ],
+            'password' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Password of the database user to authenticate',
+                'default' => null,
+                'example' => 'mySecretDatabasePassword',
+            ],
+            'db' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Database name to connect',
+                'default' => null,
+                'example' => 'wordpress',
+            ],
+            'timeout' => [
+                'type' => 'float',
+                'required' => false,
+                'description' => 'Timeout in sec',
+
+                'default' => 5,
+                'example' => '3',
+            ],
+        ],
+    ];
+
     /**
      * Get default group of this check
      * @return string
@@ -59,14 +120,22 @@ class checkMysqlConnect extends appmonitorcheck
             return [RESULT_ERROR, 'ERROR: setting mysqli_options failed.'];
         }
 
-        $db = (isset($aParams["port"]) && $aParams["port"])
-            ? $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"])
-            : $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"])
-        ;
-        if ($db) {
-            $mysqli->close();
-            return [RESULT_OK, "OK: Mysql database " . $aParams["db"] . " was connected"];
-        } else {
+        try{
+
+            $db = (isset($aParams["port"]) && $aParams["port"])
+                ? $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"], $aParams["port"])
+                : $mysqli->real_connect($aParams["server"], $aParams["user"], $aParams["password"], $aParams["db"])
+            ;
+            if ($db) {
+                $mysqli->close();
+                return [RESULT_OK, "OK: Mysql database " . $aParams["db"] . " was connected"];
+            } else {
+                return [
+                    RESULT_ERROR,
+                    "ERROR: Mysql database " . $aParams["db"] . " was not connected. Error " . mysqli_connect_errno() . ": " . mysqli_connect_error()
+                ];
+            }
+        } catch (Exception $e) {
             return [
                 RESULT_ERROR,
                 "ERROR: Mysql database " . $aParams["db"] . " was not connected. Error " . mysqli_connect_errno() . ": " . mysqli_connect_error()
diff --git a/public_html/appmonitor/plugins/checks/pdoconnect.php b/public_html/appmonitor/plugins/checks/pdoconnect.php
index d95b3d55074553181d307f68e59bdcfcaba9ab1f..a3915015e63bc308f696bcdc4270863051d2eb64 100755
--- a/public_html/appmonitor/plugins/checks/pdoconnect.php
+++ b/public_html/appmonitor/plugins/checks/pdoconnect.php
@@ -19,10 +19,52 @@
  * 
  * 2021-10-27  <axel.hahn@iml.unibe.ch>
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
- * 
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkPdoConnect extends appmonitorcheck
 {
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin PdoConnect',
+        'description' => 'Verify a database connection with PDO connect',
+        'parameters' => [
+            'connect' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => 'PDO conect string. See http://php.net/manual/en/pdo.drivers.php',
+
+                // doc
+                'default' => null,
+                'example' => 'mysql:host=$aDb[server];port=3306;dbname=$aDb[database]',
+            ],
+            'user' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Database user to connect with',
+                'default' => null,
+                'example' => 'dbuser',
+            ],
+            'password' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Password of the database user to authenticate',
+                'default' => null,
+                'example' => 'mySecretDatabasePassword',
+            ],
+            'timeout' => [
+                'type' => 'float',
+                'required' => false,
+                'description' => 'Timeout in sec',
+
+                'default' => 5,
+                'example' => '3',
+            ],
+        ],
+    ];
+
     /**
      * Get default group of this check
      * @return string
@@ -31,6 +73,7 @@ class checkPdoConnect extends appmonitorcheck
     {
         return 'database';
     }
+
     /**
      * Check connection to a database using pdo
      * see http://php.net/manual/en/pdo.drivers.php
diff --git a/public_html/appmonitor/plugins/checks/phpmodules.php b/public_html/appmonitor/plugins/checks/phpmodules.php
index b4b8170aa02b816070867397ddc884bfa3dc1557..547ec2d64f07d110df3aa11b68fc480571a3f311 100644
--- a/public_html/appmonitor/plugins/checks/phpmodules.php
+++ b/public_html/appmonitor/plugins/checks/phpmodules.php
@@ -19,10 +19,37 @@
  * 
  * 2022-05-06  <axel.hahn@iml.unibe.ch>  first lines
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
- * 
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkPhpmodules extends appmonitorcheck
 {
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin Phpmodules',
+        'description' => 'Check loaded php modules',
+        'parameters' => [
+            'required' => [
+                'type' => 'array',
+                'required' => true,
+                'description' => 'List of php modules that are required',
+
+                // doc
+                'default' => [],
+                'example' => '["curl", "PDO"]',
+            ],
+            'optional' => [
+                'type' => 'array',
+                'required' => false,
+                'description' => 'List of php modules that are optional. If one is missing, the status is set to warning.',
+                'default' => [],
+                'example' => '["gd"]',
+            ],
+        ],
+    ];
+
     /**
      * Get default group of this check
      * @return string
diff --git a/public_html/appmonitor/plugins/checks/ping.php b/public_html/appmonitor/plugins/checks/ping.php
index 3056314ccbedca581d82c8ae204305046aa37130..f1acd56eaad15e2308781784f7d8f1a5f7dde45d 100644
--- a/public_html/appmonitor/plugins/checks/ping.php
+++ b/public_html/appmonitor/plugins/checks/ping.php
@@ -21,9 +21,32 @@
  * 2022-09-16  <axel.hahn@iml.unibe.ch>  read error before closing socket.
  * 2022-11-22  <axel.hahn@iml.unibe.ch>  Use exec with detecting MS Win for the ping parameter for count of pings
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkPing extends appmonitorcheck
 {
+
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin Ping',
+        'description' => 'Check if a given host can be pinged.',
+        'parameters' => [
+            'host' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Hostname to ping; default: 127.0.0.1',
+                'regex' => '/^[a-z0-9\_\-\.]/i',
+
+                // doc
+                'default' => "127.0.0.1",
+                'example' => 'www.example.com',
+            ],
+        ],
+    ];
+
     /**
      * Get default group of this check
      * @return string
diff --git a/public_html/appmonitor/plugins/checks/porttcp.php b/public_html/appmonitor/plugins/checks/porttcp.php
index bd47ce5fbdf5919891c239debe68887a6b6b87da..879c3469a0bafcd23db981e2d0c70695040aea9b 100755
--- a/public_html/appmonitor/plugins/checks/porttcp.php
+++ b/public_html/appmonitor/plugins/checks/porttcp.php
@@ -22,10 +22,46 @@
  * 2022-09-16  <axel.hahn@iml.unibe.ch>  read error before closing socket.
  * 2022-12-05  <axel.hahn@unibe.ch>      add @ sign at socket functions to prevent warning
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
- * 
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkPortTcp extends appmonitorcheck
 {
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin PortTcp',
+        'description' => 'Check if the local server or another host is listening to a given port number.',
+        'parameters' => [
+            'host' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => 'optional: hostname to connect to; if unavailable 127.0.0.1 will be tested',
+                'regex' => '/./',
+
+                // doc
+                'default' => null,
+                'example' => 'mysql:host=$aDb[server];port=3306;dbname=$aDb[database]',
+            ],
+            'port' => [
+                'type' => 'int',
+                'required' => true,
+                'description' => 'port number to check',
+                'default' => null,
+                'validate' => 'portnumber',
+                'example' => '22',
+            ],
+            'timeout' => [
+                'type' => 'int',
+                'required' => false,
+                'description' => 'optional timeout in sec; default: 5',
+                'default' => 5,
+                'example' => '3',
+            ],
+        ],
+    ];
+
     /**
      * Get default group of this check
      * @return string
diff --git a/public_html/appmonitor/plugins/checks/simple.php b/public_html/appmonitor/plugins/checks/simple.php
index 3af69b69008682e7a91ed9cfe2be93ebc27b30ed..f488ccaae5a053c500a4dd41a3b4e7119b5d4310 100755
--- a/public_html/appmonitor/plugins/checks/simple.php
+++ b/public_html/appmonitor/plugins/checks/simple.php
@@ -19,10 +19,60 @@
  * 
  * 2021-10-27  <axel.hahn@iml.unibe.ch>
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
- * 
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkSimple extends appmonitorcheck
 {
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin Simple',
+        'description' => 'Check loaded php modules',
+        'parameters' => [
+            'value' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => 'Ouput text to describe the result',
+                'default' => "",
+                'example' => 'My simple example',
+            ],
+            'result' => [
+                'type' => 'int',
+                'required' => true,
+                'description' => 'Result value to return',
+                'min' => RESULT_OK,
+                'max' => RESULT_ERROR,
+
+                // doc
+                'default' => RESULT_OK,
+                'example' => '0 (for "ok")',
+            ],
+            'type' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Type of the rendetred tile',
+                'oneof' => ["counter"],
+                'default' => null,
+                'example' => 'counter',
+            ],
+            'count' => [
+                'type' => 'float',
+                'required' => false,
+                'description' => 'If a count exists in a check then a tile will be rendered as a tile',
+                'default' => null,
+                'example' => '3.14',
+            ],
+            'visual' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Visualize count value with "simple", "line", "bar,<width>,<count>"',
+                'default' => null,
+                'example' => 'bar,3,100',
+            ],
+        ],
+    ];
 
     /**
      * Most simple check: set given values
@@ -36,8 +86,8 @@ class checkSimple extends appmonitorcheck
      *     brainstorming for a future release
      * 
      *     "counter"  optioal: array of counter values
-     *         - label         string   a label
-     *         - value         float    a number
+     *         - type          string   a label
+     *         - count         float    a number
      *         - type          string   one of simple | bar | line
      * @return array
      */
diff --git a/public_html/appmonitor/plugins/checks/sqliteconnect.php b/public_html/appmonitor/plugins/checks/sqliteconnect.php
index 703db60d7467d1ff6968b659e0d2c3ca8c2d5c22..fcbda105782872bbeb1933100ae25c24458fb8cc 100755
--- a/public_html/appmonitor/plugins/checks/sqliteconnect.php
+++ b/public_html/appmonitor/plugins/checks/sqliteconnect.php
@@ -19,10 +19,54 @@
  * 
  * 2021-10-27  <axel.hahn@iml.unibe.ch>
  * 2024-07-23  <axel.hahn@unibe.ch>      php 8 only: use typed variables
- * 
+ * 2025-03-19  <axel.hahn@unibe.ch>      add validation rules and parameter description
  */
 class checkSqliteConnect extends appmonitorcheck
 {
+
+    /**
+     * Self documentation and validation rules
+     * @var array
+     */
+    protected array $_aDoc = [
+        'name' => 'Plugin SqliteConnect',
+        'description' => 'Verify a database connection with PDO connect',
+        'parameters' => [
+            'db' => [
+                'type' => 'string',
+                'required' => true,
+                'description' => 'full path of the sqlite database file',
+                'regex' => '/./',
+
+                // doc
+                'default' => null,
+                'example' => 'mysql:host=$aDb[server];port=3306;dbname=$aDb[database]',
+            ],
+            'user' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Database user to connect with',
+                'default' => null,
+                'example' => 'dbuser',
+            ],
+            'password' => [
+                'type' => 'string',
+                'required' => false,
+                'description' => 'Password of the database user to authenticate',
+                'default' => null,
+                'example' => 'mySecretDatabasePassword',
+            ],
+            'timeout' => [
+                'type' => 'float',
+                'required' => false,
+                'description' => 'Timeout in sec',
+
+                'default' => 5,
+                'example' => '3',
+            ],
+        ],
+    ];
+
     /**
      * Get default group of this check
      * @return string