diff --git a/cm.sh b/cm.sh
index 28b9ab77d6c56199b7fc2d73ccb74917d12a1ff2..7ea28f983b3d0da660374655743b169b34d66380 100755
--- a/cm.sh
+++ b/cm.sh
@@ -37,6 +37,7 @@
 # 2023-02-01  <axel.hahn@unibe.ch>      skip function _fixKeyfile with former workaround
 # 2023-05-08  <axel.hahn@unibe.ch>      fix: "key and cert do not match"
 # 2024-03-21  <axel.hahn@unibe.ch>      shorter sleep times
+# 2025-01-08  <axel.hahn@unibe.ch>      support for http challenge on port 80; more params supported for force renew, http challenge
 # ======================================================================
 
 
@@ -46,6 +47,8 @@
 #
 # ----------------------------------------------------------------------
 
+_version="2025-01-08"
+
 logdir="./log"
 touchfile="$logdir/lastchange.txt"
 logfile="$logdir/certmanager.log"
@@ -67,6 +70,12 @@ CM_writedebuglog=0
 
 CM_timer_start=$( date +%s.%N )
 
+# webroot to write challenge files into -
+CM_webroot=""
+
+# force renew (default: disabled - remember quotas on LE)
+CM_force=0
+
 # ----------------------------------------------------------------------
 #
 # INTERNAL FUNCTIONS
@@ -76,58 +85,58 @@ CM_timer_start=$( date +%s.%N )
 # BUGFIX: acme.sh does not create a new key file on renew.
 # After switching from csr method to param -d we got a 0 byte Keyfile
 function _fixKeyfile(){
-	echo "SKIP: _fixKeyfile won't be executed anymore."
-	# local _acme_keyfile=~/.acme.sh/${CM_fqdn}/${CM_fqdn}.key
-	# if test ! -f "$_acme_keyfile"
-	# then
-	# 	echo "FIX: copy key from csr folder $CM_filekey to $_acme_keyfile"
-	# 	if ! cp "$CM_filekey" "$_acme_keyfile"
-	# 	then
-	# 		exit 1
-	# 	fi
-	# fi
+    echo "SKIP: _fixKeyfile won't be executed anymore."
+    # local _acme_keyfile=~/.acme.sh/${CM_fqdn}/${CM_fqdn}.key
+    # if test ! -f "$_acme_keyfile"
+    # then
+    #       echo "FIX: copy key from csr folder $CM_filekey to $_acme_keyfile"
+    #       if ! cp "$CM_filekey" "$_acme_keyfile"
+    #       then
+    #           exit 1
+    #       fi
+    # fi
 }
 
 # internal function; list certificates incl. creation date and renew date
 function _listCerts(){
-	$ACME --list	
+    $ACME --list
 }
 
 # internal function; get a list of fqdn of all existing certs
 function _listCertdomains(){
-	_listCerts | sed -n '2,$p' | awk '{ print $1 }'
+    _listCerts | sed -n '2,$p' | awk '{ print $1 }'
 }
 
 # internal function; checks if a certificate for a given FQDN already exists
 # used in _certMustExist, _certMustNotExist
 # param  string  FQDN
 function _certExists(){
-	# _listCertdomains | grep "^${CM_fqdn}$" >/dev/null
-	$ACME --info -d "${CM_fqdn}" 2>/dev/null | grep "letsencrypt.org" >/dev/null
+    # _listCertdomains | grep "^${CM_fqdn}$" >/dev/null
+    $ACME --info -d "${CM_fqdn}" 2>/dev/null | grep "letsencrypt.org" >/dev/null
 }
 
 # internal function; a certificate of a given FQDN must exist - otherwise
 # the script will be aborted
 # param  string  FQDN
 function _certMustExist(){
-	_certExists
-	if [ $? -ne 0 ]; then
-		echo "ERROR: cert ${CM_fqdn} was not added yet."
-		exit 1
-	fi
+    _certExists
+    if [ $? -ne 0 ]; then
+        _we "Cert ${CM_fqdn} was not added yet."
+        exit 1
+    fi
 }
 
 # internal function; a certificate of a given FQDN must not exist - otherwise
 # the script will be aborted
 # param  string  FQDN
 function _certMustNotExist(){
-	if _certExists
-	then
-		echo "ERROR: cert ${CM_fqdn} was added already."
-		# exit 1
-		echo "Press Ctrl+C to abort within the next 3 sec..."
-		sleep 3
-	fi
+    if _certExists
+    then
+        _we "Cert ${CM_fqdn} was added already."
+        # exit 1
+        echo "Press Ctrl+C to abort within the next 3 sec..."
+        sleep 3
+    fi
 }
 
 # internal function: transfer generated/ updated cert data to a
@@ -135,35 +144,35 @@ function _certMustNotExist(){
 # used in public_add and public_renew
 # used in ADD and RENEW action
 function _certTransfer(){
-	_wd "--- acme internal data - ~/.acme.sh/${CM_fqdn}"
-	ls -l ~/.acme.sh/${CM_fqdn}
+    _wd "--- acme internal data - ~/.acme.sh/${CM_fqdn}"
+    ls -l ~/.acme.sh/${CM_fqdn}
 
-	_wd "--- delete current files in ${CM_dircerts}/ if they already exist."
+    _wd "--- delete current files in ${CM_dircerts}/ if they already exist."
     test -d ${CM_dircerts} && rm -f "${CM_dircerts}/*" 2>/dev/null
 
-	_wd "--- transfer acme.sh files to ${CM_dircerts}/"
-	if ! $ACME \
-		--install-cert \
-		-d "${CM_fqdn}" \
-		--key-file        "${CM_outfile_key}"   \
-		--cert-file       "${CM_outfile_cert}"  \
-		--fullchain-file  "${CM_outfile_chain}" \
-		--ca-file         "${CM_outfile_ca}"
+    _wd "--- transfer acme.sh files to ${CM_dircerts}/"
+    if ! $ACME \
+        --install-cert \
+        -d "${CM_fqdn}" \
+        --key-file        "${CM_outfile_key}"   \
+        --cert-file       "${CM_outfile_cert}"  \
+        --fullchain-file  "${CM_outfile_chain}" \
+        --ca-file         "${CM_outfile_ca}"
     then
         echo "ERROR occured during acme transfer. Removing files in ${CM_dircerts} to prevent strange effects..."
         rm -f "${CM_dircerts}/*"
         exit 2
     fi
-	echo "OK."
-	
-	# _wd "--- copy key to ${CM_dircerts}"
-	# cp ${CM_filekey} ${CM_outfile_key}
+        echo "OK."
+
+        # _wd "--- copy key to ${CM_dircerts}"
+        # cp ${CM_filekey} ${CM_outfile_key}
 
-	_wd "--- create chained file for haproxy"
-	cat "${CM_outfile_chain}" "${CM_outfile_key}" > "${CM_outfile_haproxy}"
+        _wd "--- create chained file for haproxy"
+        cat "${CM_outfile_chain}" "${CM_outfile_key}" > "${CM_outfile_haproxy}"
 
-	_wd "--- content of output dir $CM_dircerts:"
-	if ! ls -l "${CM_outfile_cert}" "${CM_outfile_chain}" "${CM_outfile_key}" "${CM_outfile_haproxy}"
+        _wd "--- content of output dir $CM_dircerts:"
+        if ! ls -l "${CM_outfile_cert}" "${CM_outfile_chain}" "${CM_outfile_key}" "${CM_outfile_haproxy}"
     then
         echo "ERROR missing a file (or no access?)"
         rm -f "${CM_dircerts}/*"
@@ -174,23 +183,23 @@ function _certTransfer(){
 # internal function; show md5 hashsums for certificate and key
 # for visual comparison if the match
 function _certMatching(){
-	# CSR USAGE WAS REMOVED
-	# local md5_csr=$(  test -f ${CM_filecsr}      && openssl req  -noout -modulus -in ${CM_filecsr}      | openssl md5 | cut -f 2 -d " " )
-	local md5_key=$(  test -f ${CM_outfile_key}  && openssl rsa  -noout -modulus -in ${CM_outfile_key}  | openssl md5 | cut -f 2 -d " " )
-	local md5_cert=$( test -f ${CM_outfile_cert} && openssl x509 -noout -modulus -in ${CM_outfile_cert} | openssl md5 | cut -f 2 -d " " )
-
-	echo
-	echo "--- compare hashes"
-	# CSR USAGE WAS REMOVED
-	# echo "csr  : $md5_csr (used for creation of cert)"
-	echo "key  : $md5_key"
-	echo "cert : $md5_cert"
-	if [ "$md5_key" = "$md5_cert" ]; then
-		echo "OK, key and cert match :-)"
-	else
-		echo "ERROR: key and cert do NOT MATCH!"
-	fi
-	echo
+    # CSR USAGE WAS REMOVED
+    # local md5_csr=$(  test -f ${CM_filecsr}      && openssl req  -noout -modulus -in ${CM_filecsr}      | openssl md5 | cut -f 2 -d " " )
+    local md5_key; md5_key=$(  test -f ${CM_outfile_key}  && openssl rsa  -noout -modulus -in ${CM_outfile_key}  | openssl md5 | cut -f 2 -d " " )
+    local md5_cert; md5_cert=$( test -f ${CM_outfile_cert} && openssl x509 -noout -modulus -in ${CM_outfile_cert} | openssl md5 | cut -f 2 -d " " )
+
+    echo
+    echo "--- compare hashes"
+    # CSR USAGE WAS REMOVED
+    # echo "csr  : $md5_csr (used for creation of cert)"
+    echo "key  : $md5_key"
+    echo "cert : $md5_cert"
+    if [ "$md5_key" = "$md5_cert" ]; then
+        echo "OK, key and cert match :-)"
+    else
+        _we "Key and cert do NOT MATCH!"
+    fi
+    echo
 }
 
 # internal function: dig for given fqdn.
@@ -202,15 +211,15 @@ function _certMatching(){
 # param  string  optional filter on output of dig (regex)
 function _checkDig(){
     local myfqdn=$1
-	local _type=${2:-"a"}
-	local _verify=${3:-"."}
+        local _type=${2:-"a"}
+        local _verify=${3:-"."}
 
     if which dig >/dev/null
     then
         # _wd "[$myfqdn] exists as type [$_type] in DNS?"
         if ! dig "${myfqdn}" "${_type}" | grep "^${myfqdn}" | grep -E "${_verify}"
         then
-            echo "ERROR: [$myfqdn] was not found. Maybe there is a typo in the hostname or it does not exist in DNS."
+            _we "[$myfqdn] was not found. Maybe there is a typo in the hostname or it does not exist in DNS."
             exit 2
         fi
         _wd "OK: [$myfqdn] exists in DNS."
@@ -228,24 +237,23 @@ function _checkDig(){
 # Function stops if a fqdn was not found in DNS.
 # param  string  fqdn(s) that are part of the certificate
 function _dnsCheck(){
-	local altdns=
-	local _mydomain=
-	local _subdomain='_acme-challenge'
-
-	for _mydomain in $*
-	do
-		_wd "dig check - domain for cert"
-		_checkDig "$_mydomain" "a" "IN.*(A|CNAME)" # matches A and CNAME records
+    local altdns=
+    local _mydomain=
+    local _subdomain='_acme-challenge'
 
-		if [ -n "${CM_challenge_alias}" ] && ! echo "$_mydomain" | grep "${CM_certmatch}" >/dev/null
-		then
-			_wd "dig check - cname ${_subdomain}.${_mydomain} must exist"
-			_checkDig "${_subdomain}.${_mydomain}" "cname"
-			_wd "dig check - cname ${_subdomain}.${_mydomain} must point to ${_subdomain}.${CM_challenge_alias}"
-			_checkDig "${_subdomain}.${_mydomain}" "cname" "${_subdomain}.${CM_challenge_alias}"
-		fi
-	done
+    for _mydomain in $*
+    do
+        _wd "dig check - domain for cert"
+        _checkDig "$_mydomain" "a" "IN.*(A|CNAME)" # matches A and CNAME records
 
+        if [ -n "${CM_challenge_alias}" ] && ! echo "$_mydomain" | grep "${CM_certmatch}" >/dev/null
+        then
+            _wd "dig check - cname ${_subdomain}.${_mydomain} must exist"
+            _checkDig "${_subdomain}.${_mydomain}" "cname"
+            _wd "dig check - cname ${_subdomain}.${_mydomain} must point to ${_subdomain}.${CM_challenge_alias}"
+            _checkDig "${_subdomain}.${_mydomain}" "cname" "${_subdomain}.${CM_challenge_alias}"
+        fi
+    done
 }
 
 # CSR USAGE WAS REMOVED
@@ -253,106 +261,105 @@ function _dnsCheck(){
 # this function is used in public_add
 # function _UNUSED_gencsr(){
 
-# 	local altdns=
+#       local altdns=
 
-# 	for myalt in $*
-# 	do
-# 		altdns="${altdns}DNS:$myalt,"
-# 	done
-# 	altdns=$( echo $altdns | sed "s#,\$##" )
+#       for myalt in $*
+#       do
+#               altdns="${altdns}DNS:$myalt,"
+#       done
+#       altdns=$( echo $altdns | sed "s#,\$##" )
 #     _wd "--- $CM_fqdn"
-# 	_wd "DNS alternative names: $altdns"
+#       _wd "DNS alternative names: $altdns"
 
-# 	rm -f $CM_filecnf $CM_filekey $CM_filecsr
-# 	mkdir -p "${CM_dircsr}" 2>/dev/null
+#       rm -f $CM_filecnf $CM_filekey $CM_filecsr
+#       mkdir -p "${CM_dircsr}" 2>/dev/null
 
-# 	cat $csrfile \
-# 		| sed "s#__FQDN__#$CM_fqdn#g"  		\
-# 		| sed "s#__ALTNAMES__#$altdns#g"	\
-# 		> $CM_filecnf || exit 1
+#       cat $csrfile \
+#               | sed "s#__FQDN__#$CM_fqdn#g"           \
+#               | sed "s#__ALTNAMES__#$altdns#g"        \
+#               > $CM_filecnf || exit 1
 
-# 	# generate csr
-# 	_wd "creating key and csr"
-# 	openssl req -new -config $CM_filecnf -keyout $CM_filekey -out $CM_filecsr || exit 1
+#       # generate csr
+#       _wd "creating key and csr"
+#       openssl req -new -config $CM_filecnf -keyout $CM_filekey -out $CM_filecsr || exit 1
 
-# 	# view csr
-# 	# openssl req -noout -text -in $CM_filecsr
-# 	ls -ltr $CM_filecnf $CM_filekey $CM_filecsr
+#       # view csr
+#       # openssl req -noout -text -in $CM_filecsr
+#       ls -ltr $CM_filecnf $CM_filekey $CM_filecsr
 # }
 
 # internal function; get a sorted list of DNS aliases in the current cert
 function _getAliases(){
-	_sortWords $(
-		openssl x509 -noout -text -in ${CM_outfile_cert} \
-			| grep -E "(DNS:)" \
-			| sed "s#^ *##g"  \
-			| sed "s#DNS:##g"  \
-			| sed "s#,##g"
-	)
+    _sortWords "$(
+        openssl x509 -noout -text -in ${CM_outfile_cert} \
+            | grep -E "(DNS:)" \
+            | sed "s#^ *##g"  \
+            | sed "s#DNS:##g"  \
+            | sed "s#,##g"
+    )"
 }
 
 # internal function; check if a required 2nd CLI parameter was given
 # if not the script will abort
 function _requiresFqdn(){
-	if [ -z "$CM_fqdn" ]; then
-		echo "ERROR: 2nd parameter must be a FQDN for Main_Domain."
-		exit 1
-	fi
+    if [ -z "$CM_fqdn" ]; then
+        _we "2nd parameter must be a FQDN for Main_Domain."
+        exit 1
+    fi
 }
 
 # internal function; it shows a message if the current instance uses a stage
 # server. It shows a message that it is allowed to test arround ... or to be 
 # careful with LE requests on a production system
 function _testStaging(){
-	echo $ACME_Params | grep "\-\-staging" >/dev/null
-	if [ $? -eq 0 ]; then
-		_wd "Using Let's Encrypt STAGE environment ..."
-		_wd "You can test and mess around. Do not use certs in production."
-	else
-		_wd "Using Let's Encrypt LIVE environment for production."
-		_wd "Be careful with count of connects to Let's Encrypt servers."
-	fi
-	echo
+    if echo "${ACME_Params}" | grep -- "--staging" >/dev/null; then
+        _wd "Using Let's Encrypt STAGE environment ..."
+        _wd "You can test and mess around. Do not use certs in production."
+    else
+        _wd "Using Let's Encrypt LIVE environment for production."
+        _wd "Be careful with count of connects to Let's Encrypt servers."
+    fi
+    echo
 }
 # internal function; if a user was set as CM_user then verify it with
 # current user
 function _testUser(){
-	if [ ! -z "$CM_user" ]; then
-		local _sUser=`id | cut -f 2 -d "(" | cut -f 1 -d ")"`
-		if [[ $_sUser != "$CM_user" ]]; then
-			echo "ERROR: Run this script under user [$CM_user] - not as $_sUser."
-			exit 1
-		fi
-	fi
-
+    if [ ! -z "$CM_user" ]; then
+        local _sUser
+        _sUser="$( id | cut -f 2 -d "(" | cut -f 1 -d ")")"
+        if [[ $_sUser != "$CM_user" ]]; then
+            _we "Run this script under user [$CM_user] - not as $_sUser."
+            exit 1
+        fi
+    fi
 }
 
 # set update message into access log file
 # global bool CM_writedebuglog flag to write access log.
 # param  string(s)  message
 function _debuglog(){
-	if [ ${CM_writedebuglog} -eq 1 ]; then
-		echo "$( date ) $CM_fqdn [$$] | $(show_timer) | $*" >> ${debuglogfile}
-	fi
+    if [ ${CM_writedebuglog} -eq 1 ]; then
+        echo "$( date ) $CM_fqdn [$$] | $(show_timer) | $*" >> ${debuglogfile}
+    fi
 }
 
 
 # set update message in a file
 # param  string(s)  message
 function _update(){
-	echo "[$( date )] $*" > ${touchfile}
-	test ${CM_writelog} -ne 0 && echo "[$( date )] $*" >> ${logfile}
+    echo "[$( date )] $*" > ${touchfile}
+    test ${CM_writelog} -ne 0 && echo "[$( date )] $*" >> ${logfile}
 }
 
 # "neverending" loop that waits until the current process is
 # the one with lowest PID
 function _wait_for_free_slot(){
     local _bWait=true
-	_debuglog "start in _wait_for_free_slot"
+    _debuglog "start in _wait_for_free_slot"
     typeset -i local _iFirstPID=0
     typeset -i local _iPos=0
     local _sProcesses
-	
+
 	_wd "--- Need to wait until own process PID $$ is on top ... "
     while [ $_bWait = true ];
     do
@@ -374,69 +381,180 @@ function _wait_for_free_slot(){
             sleep $((3 + RANDOM % 3));
         fi
     done
-	_debuglog "end _wait_for_free_slot"
+    _debuglog "end _wait_for_free_slot"
 }
 
 # write debug output if CM_showdebug is set to 1
+# param  string  message (prefix "DEBUG" will be added in front)
 function _wd(){
-	test ${CM_showdebug} -ne 0 && echo "DEBUG: $*"
+    test ${CM_showdebug} -ne 0 && echo -e "\e[1;30mDEBUG: $* \e[0m"
+}
+
+
+# show error message;
+# param  string  message (prefix "ERROR" will be added in front)
+function _we(){
+    echo -e "\e[1;37;41mERROR: $*\e[0m"
 }
 
 # set environment for a single certificate based on FQDN
 # param  string  FQDN
 function _setenv(){
-	CM_fqdn=$1
-	# CSR USAGE WAS REMOVED
-	# keeping vars to delete files of existing certs that used a csr
-	CM_filecsr="${CM_dircsr}/${CM_fqdn}.csr"
-	CM_filecnf="${CM_dircsr}/${CM_fqdn}.cnf"
-	CM_filekey="${CM_dircsr}/${CM_fqdn}.key"
-
-	CM_dircerts="${CM_diracme}/${CM_fqdn}"
-	CM_outfile_cert=${CM_dircerts}/${CM_fqdn}.cert.cer
-	CM_outfile_chain=${CM_dircerts}/${CM_fqdn}.fullchain.cer
-	CM_outfile_key=${CM_dircerts}/${CM_fqdn}.key.pem
-	CM_outfile_haproxy=${CM_dircerts}/${CM_fqdn}.haproxy.pem
-	CM_outfile_ca=${CM_dircerts}/${CM_fqdn}.ca.cer
-
-	# echo $CM_fqdn; set | grep "^CM_"; echo
-
+    CM_fqdn=$1
+    # CSR USAGE WAS REMOVED
+    # keeping vars to delete files of existing certs that used a csr
+    CM_filecsr="${CM_dircsr}/${CM_fqdn}.csr"
+    CM_filecnf="${CM_dircsr}/${CM_fqdn}.cnf"
+    CM_filekey="${CM_dircsr}/${CM_fqdn}.key"
+
+    CM_dircerts="${CM_diracme}/${CM_fqdn}"
+    CM_outfile_cert=${CM_dircerts}/${CM_fqdn}.cert.cer
+    CM_outfile_chain=${CM_dircerts}/${CM_fqdn}.fullchain.cer
+    CM_outfile_key=${CM_dircerts}/${CM_fqdn}.key.pem
+    CM_outfile_haproxy=${CM_dircerts}/${CM_fqdn}.haproxy.pem
+    CM_outfile_ca=${CM_dircerts}/${CM_fqdn}.ca.cer
+
+    # echo $CM_fqdn; set | grep "^CM_"; echo
 }
+
 # internal function; helper: sort words in alphabetic order
 function _sortWords(){
-	echo $* | tr " " "\n" | sort | tr "\n" " "
+    echo "$*" | tr " " "\n" | sort | tr "\n" " "
 }
 
 # internal function; verify fqdn in cli params - each fqdn is allowed only once.
 # on error it shows the count of usage of each fqdn
 function _testFqdncount(){
-	typeset -i local iHostsInParam=$( echo $* | wc -w )
-	typeset -i iHostsUniq=$( echo $* | tr " " "\n" | sort -u | wc -w )
-	if [ $iHostsInParam -ne $iHostsUniq ]; then
-		echo "ERROR: each given FQDN is allowed only once. You need to remove double entries."
-		
-		for myhost in $( echo $* | tr " " "\n" | sort -u )
-		do
-			typeset -i iHostcount=$( echo $* | tr " " "\n" | grep "^$myhost$" | wc -l )
-			test $iHostcount -gt 1 && echo "  $iHostcount x $myhost"
-		done
-		echo
-		exit 1
-	fi
+    typeset -i local iHostsInParam=$( echo $* | wc -w )
+    typeset -i iHostsUniq=$( echo $* | tr " " "\n" | sort -u | wc -w )
+    if [ $iHostsInParam -ne $iHostsUniq ]; then
+        _we "Each given FQDN is allowed only once. You need to remove double entries."
+
+        for myhost in $( echo $* | tr " " "\n" | sort -u )
+        do
+            typeset -i iHostcount=$( echo $* | tr " " "\n" | grep "^$myhost$" | wc -l )
+            test $iHostcount -gt 1 && echo "  $iHostcount x $myhost"
+        done
+        echo
+        exit 1
+    fi
 }
 
 # get time in sec and milliseconds since start
 # no parameter is required
 function show_timer(){
-         local timer_end=$( date +%s.%N )
-         local totaltime=$( awk "BEGIN {print $timer_end - $CM_timer_start }" )
+    local timer_end; timer_end=$( date +%s.%N )
+    local totaltime; totaltime=$( awk "BEGIN {print $timer_end - $CM_timer_start }" )
+
+    local sec_time; sec_time=$( echo "$totaltime" | cut -f 1 -d "." )
+    test -z "$sec_time" && sec_time=0
+
+    local ms_time; ms_time=$( echo "$totaltime" | cut -f 2 -d "." | cut -c 1-3 )
+
+    echo "$sec_time.$ms_time sec"
+}
+
+# show help text
+function show_help(){
+        self="$( basename "$0" )"
+        cat <<EOF
 
-         local sec_time=$( echo $totaltime | cut -f 1 -d "." )
-         test -z "$sec_time" && sec_time=0
+HELP
+
+Wrapper script for acme.sh to handle certificates.
+For automation you should use the "ensure" action that detects if a 
+certificate must be created, renewed or re-created.
+
+📄 Source: <https://git-repo.iml.unibe.ch/iml-open-source/iml-certman>
+📜 License: GNU GPL 3.0
+📗 Docs: <https://os-docs.iml.unibe.ch/iml-certman/>
+
+SYNTAX:
+
+    $self [OPTIONS] ACTION <FQDN> [<ALIASES>]
+
+OPTIONS:
+
+    -a|--alias
+        Use http challenge with existing http server on port 80
+        Challenge file will be written into ../alias-dir/
+
+    -f|--force
+        Force renew of certificate even if it is not due yet.
+        Use it carefully - remember the execution limits on Let's Encrypt.
+
+    -t|--trace
+        the output additionally will be written into a tracelog file 
+        below $logdir.
+
+    -v|--verbose
+        show debug infos on console.
+        Remark: for permanent usage set CM_showdebug=1 in inc_config.sh
 
-         local ms_time=$( echo $totaltime | cut -f 2 -d "." | cut -c 1-3 )
+    -w|--webroot <DIR>
+        Use http challenge with existing http server on port 80
+        Challenge file will be written into given directory
 
-         echo "$sec_time.$ms_time sec"
+The ACTIONs for SINGLE certificate handlings are:
+
+    add <FQDN> [.. <FQDN-N>] 
+        create new certificate
+        The first FQDN is a hostname to generate the certificate for. 
+        Following multiple hostnames will be used as DNS aliases in the 
+        same certificate.
+        It updates files in ${CM_diracme}
+
+    ensure <FQDN> [.. <FQDN-N>] 
+        It ensures that a certificate with given aliases exists and is up to date.
+        This param is for simple usage in automation tools like Ansible or Puppet.
+        It is required to add all aliases as parameters what is unhandy for
+        direct usage on cli.
+
+        If the cert does not exist it will be created (see "add").
+        If fqdn and aliases are the same like in the certificate it performs a renew.
+        If fqdn and aliases differ:
+        - the current certificate will be rejected + deleted (see "delete")
+        - a new certificate will be added ()
+
+    delete <FQDN>
+        delete all files of a given certificate
+
+    renew <FQDN>
+        renew (an already added) certificate
+        and update files in ${CM_diracme}
+
+    show <FQDN>
+        show place of certificate data and show basic certificate data
+        (issuer, subject, aliases, ending date)
+
+    transfer <FQDN>
+        Transfer cert from acme.sh internal cache to our output dir again.
+        It is done during add or renew. With transfer command you can repeat it.
+
+ACTIONs for ALL certs
+
+    list
+        list all certificates including creation and renew date
+
+    list-old
+        list all certificates older 65 and older 90 days and exit.
+        Exitcodes:
+            0 - all certs are up to date.
+            1 - certificates to renew were found
+            2 - outdatedt certificates were found
+
+    renew-all
+        renew all certificates (fast mode - without --force)
+        and update files in ${CM_diracme}
+        It is useful for a cronjob.
+
+other ACTIONs
+
+    selftest
+        check of health with current setup and requirements.
+        This command is helpful for initial setups.
+
+EOF
 }
 
 # ----------------------------------------------------------------------
@@ -449,230 +567,247 @@ function show_timer(){
 # pulic function ADD certificate
 # 
 function public_add(){
-	local _params=""
+    local _params=""
 
-	_debuglog "start public_add"
-	_wait_for_free_slot
-	_requiresFqdn
+    _debuglog "start public_add"
+    _wait_for_free_slot
+    _requiresFqdn
     _certMustNotExist
 
-	_dnsCheck $CM_fqdn $*
+    _dnsCheck $CM_fqdn $*
 
-	for _mydomain in $CM_fqdn $*
-	do
-		_params+="-d $_mydomain --challenge-alias "
+    local iCounter
+    typeset -i iCounter=0
+    for _mydomain in $CM_fqdn $*
+    do
+        iCounter+=1
+        _params+="-d $_mydomain "
 
-		if [ -n "${CM_challenge_alias}" ] && ! echo "$_mydomain" | grep "${CM_certmatch}" >/dev/null
-		then
-			_params+="${CM_challenge_alias} "
-		else
-			_params+="no "
-		fi
-	done
+        if [ -n "$CM_webroot" ]
+        then
+            echo "WEBROOT found"
+            if [ $iCounter -eq 1 ]
+            then
+                _params+="-w $CM_webroot "
+            fi
+        else
+            if [ -n "${CM_challenge_alias}" ]
+            then
+                _params+=" --challenge-alias "
+                if ! echo "$_mydomain" | grep "${CM_certmatch}" >/dev/null 
+                then
+                    _params+="${CM_challenge_alias} "
+                else
+                    _params+="no "
+                fi
+            fi
+        fi
+    done
 
-	# 2023-05-08: Specifies the domain key length
-	_params+="--keylength 2048 "
+    # 2023-05-08: Specifies the domain key length
+    _params+="--keylength 2048 "
 
-	_wd "--- create output dir $dircerts"
-	mkdir -p "${CM_dircerts}" 2>/dev/null
+    _wd "--- create output dir $dircerts"
+    mkdir -p "${CM_dircerts}" 2>/dev/null
 
-	_wd "--- create certificate"
-	echo "$ACME --issue $_params $ACME_Params"
-	if ! $ACME --issue $_params $ACME_Params 
-	then
-		echo "ERROR: adding cert failed. Trying to delete internal data ..."
-		public_delete $CM_fqdn
-		exit 1
-	fi
-	# $ACME --issue -d $CM_fqdn $ACME_Params || exit 1
+    _wd "--- create certificate"
+    echo "$ACME --issue $_params $ACME_Params"
+    if ! $ACME --issue $_params $ACME_Params 
+    then
+        _we "Adding cert failed. Trying to delete internal data ..."
+        public_delete "$CM_fqdn"
+        exit 1
+    fi
+    # $ACME --issue -d $CM_fqdn $ACME_Params || exit 1
 
-	_certTransfer
-	_certMatching
+    _certTransfer
+    _certMatching
 
-	_update "added $CM_fqdn $*"
-	_debuglog "end public_add"
+    _update "added $CM_fqdn $*"
+    _debuglog "end public_add"
 }
 
 # CSR USAGE WAS REMOVED
 # function OLD__public_add(){
-# 	_wait_for_free_slot
-# 	_requiresFqdn
+#       _wait_for_free_slot
+#       _requiresFqdn
 #     _certMustNotExist
 
-# 	for myhost in $( echo $CM_fqdn $*)
-# 	do 
-# 		echo $myhost | grep "$CM_certmatch" >/dev/null
-# 		if [ $? -ne 0 ]; then
-# 			echo "ERROR: host $myhost does not match [$CM_certmatch]."
-# 			exit 1
-# 		fi
-# 	done
-# 	_gencsr $CM_fqdn $*
-
-# 	_wd "--- create output dir $dircerts"
-# 	mkdir -p "${CM_dircerts}" 2>/dev/null
-
-# 	_wd "--- csr data"
-# 	$ACME --showcsr  --csr $CM_filecsr || exit 1
-
-# 	_wd "--- create certificate"
-# 	echo $ACME --signcsr --csr $CM_filecsr $ACME_Params 
-# 	$ACME --signcsr --csr $CM_filecsr $ACME_Params 
-# 	if [ $? -ne 0 ]; then
-# 		echo "ERROR: adding cert failed. Trying to delete internal data ..."
-# 		public_delete $CM_fqdn
-# 		exit 1
-# 	fi
-# 	# $ACME --issue -d $CM_fqdn $ACME_Params || exit 1
-
-# 	_certTransfer
-# 	_certMatching
-
-# 	_update "added $CM_fqdn $*"
+#       for myhost in $( echo $CM_fqdn $*)
+#       do 
+#               echo $myhost | grep "$CM_certmatch" >/dev/null
+#               if [ $? -ne 0 ]; then
+#                       echo "ERROR: host $myhost does not match [$CM_certmatch]."
+#                       exit 1
+#               fi
+#       done
+#       _gencsr $CM_fqdn $*
+
+#       _wd "--- create output dir $dircerts"
+#       mkdir -p "${CM_dircerts}" 2>/dev/null
+
+#       _wd "--- csr data"
+#       $ACME --showcsr  --csr $CM_filecsr || exit 1
+
+#       _wd "--- create certificate"
+#       echo $ACME --signcsr --csr $CM_filecsr $ACME_Params 
+#       $ACME --signcsr --csr $CM_filecsr $ACME_Params 
+#       if [ $? -ne 0 ]; then
+#               echo "ERROR: adding cert failed. Trying to delete internal data ..."
+#               public_delete $CM_fqdn
+#               exit 1
+#       fi
+#       # $ACME --issue -d $CM_fqdn $ACME_Params || exit 1
+
+#       _certTransfer
+#       _certMatching
+
+#       _update "added $CM_fqdn $*"
 # }
 
 #
 # pulic function ADD OR RENEW certificate
 # 
 function public_ensure(){
-	_wait_for_free_slot
-	_requiresFqdn
-	_certExists
-	if [ $? -eq 0 ]; then
-		_wd "--- cert $CM_fqdn was found ... compare aliases"
-		local _newAliases=$( _sortWords $CM_fqdn $* )
-		local _certAliases=$( _getAliases )
-
-		_wd "from params: $_newAliases"
-		_wd "inside cert: $_certAliases"
-		if [ "$_newAliases" = "$_certAliases" ]; then
-			_wd "--- DNS aliases match ... renew it (ignore --force - it comes from acme.sh)"
-			public_renew $*
-		else
-			# _wd "--- DNS aliases do NOT match ... deleting cert and create a new one"
-			# public_delete $*
-			_wd "--- DNS aliases do NOT match ... creating a new one"
-			_fixKeyfile
-			public_add $*
-		fi
-	else
-		_wd "--- cert does mot exist ... add it"
-		public_add $*
-	fi
+    _wait_for_free_slot
+    _requiresFqdn
+    _certExists
+    if [ $? -eq 0 ]; then
+        _wd "--- cert $CM_fqdn was found ... compare aliases"
+        local _newAliases; _newAliases=$( _sortWords $CM_fqdn $* )
+        local _certAliases; _certAliases=$( _getAliases )
+
+        _wd "from params: $_newAliases"
+        _wd "inside cert: $_certAliases"
+        if [ "$_newAliases" = "$_certAliases" ]; then
+            _wd "--- DNS aliases match ... renew it (ignore --force - it comes from acme.sh)"
+            public_renew $*
+        else
+            # _wd "--- DNS aliases do NOT match ... deleting cert and create a new one"
+            # public_delete $*
+            _wd "--- DNS aliases do NOT match ... creating a new one"
+            _fixKeyfile
+            public_add $*
+        fi
+    else
+        _wd "--- cert does mot exist ... add it"
+        public_add $*
+    fi
 }
 
 #
 # public function to delete a cert
 #
 function public_delete(){
-	_debuglog "start public_delete"
-	_wait_for_free_slot
-	_requiresFqdn
-	_certMustExist
-
-	# TODO: revoke it too??
-	_wd "--- revoke cert"
-	$ACME --revoke -d ${CM_fqdn} $ACME_Params
-	_wd "--- delete ACME.SH data"
-	$ACME --remove -d ${CM_fqdn} $ACME_Params
-	_wd "--- delete local data"
-
-	# CSR USAGE WAS REMOVED
-	rm -rf ${CM_dircerts} ${CM_filecnf} ${CM_filekey} ${CM_filecsr} ~/.acme.sh/${CM_fqdn} && echo OK
-	_update "deleted ${CM_fqdn}"
-	_debuglog "end public_delete"
+    _debuglog "start public_delete"
+    _wait_for_free_slot
+    _requiresFqdn
+    _certMustExist
+
+    # TODO: revoke it too??
+    _wd "--- revoke cert"
+    $ACME --revoke -d ${CM_fqdn} $ACME_Params
+    _wd "--- delete ACME.SH data"
+    $ACME --remove -d ${CM_fqdn} $ACME_Params
+    _wd "--- delete local data"
+
+    # CSR USAGE WAS REMOVED
+    rm -rf ${CM_dircerts} ${CM_filecnf} ${CM_filekey} ${CM_filecsr} ~/.acme.sh/${CM_fqdn} && echo OK
+    _update "deleted ${CM_fqdn}"
+    _debuglog "end public_delete"
 }
 
-
 #
 # public function; list certificates incl. creation date and renew date
 # 
 function public_list(){
-	_listCerts
-
+    _listCerts
 }
+
 #
 # public function; list old / outdated certificates
 # 
 function public_list-old(){
+    local _iRuntime=90
+    local _iWarn=65
+    local _rc; typeset -i _rc=0
 
-	local _iRuntime=90
-	local _iWarn=65
-	typeset -i local _rc=0
-
-	cd "${CM_dircerts}"  || exit 1
-
-	echo
-	echo "Cert dir is $( pwd )"
-	echo
-	echo "---- Certificates expiring soon - with age $_iWarn ... $_iRuntime days:"
-	if find -type f -name "*.cert.cer" -mtime +$_iWarn -mtime -$_iRuntime | grep . >/dev/null
-	then
-		find -type f -name "*.cert.cer" -mtime +$_iWarn -mtime -$_iRuntime  -exec ls -ld {} \; | nl
-		_rc=1
-	else 
-		echo "     NONE."
-	fi
-	echo
-	echo "---- Certificate list ... older $_iRuntime days:"
-	if find -type f -name "*.cert.cer" -mtime +$_iRuntime | grep . >/dev/null
-	then
-		find -type f -name "*.cert.cer" -mtime +$_iRuntime -exec ls -ld {} \; | nl
-		_rc=2
-	else
-		echo "     NONE."
-	fi
-
-	echo	
-	cd - >/dev/null
-
-	_wd "Exiting with rc=$_rc"
-	exit $_rc
-}
+    cd "${CM_dircerts}"  || exit 1
+
+    echo
+    echo "Cert dir is $( pwd )"
+    echo
+    echo "---- Certificates expiring soon - with age $_iWarn ... $_iRuntime days:"
+    if find -type f -name "*.cert.cer" -mtime +$_iWarn -mtime -$_iRuntime | grep . >/dev/null
+    then
+        find -type f -name "*.cert.cer" -mtime +$_iWarn -mtime -$_iRuntime  -exec ls -ld {} \; | nl
+        _rc=1
+    else 
+        echo "     NONE."
+    fi
+    echo
+    echo "---- Certificate list ... older $_iRuntime days:"
+    if find -type f -name "*.cert.cer" -mtime +$_iRuntime | grep . >/dev/null
+    then
+        find -type f -name "*.cert.cer" -mtime +$_iRuntime -exec ls -ld {} \; | nl
+        _rc=2
+    else
+        echo "     NONE."
+    fi
 
+    echo
+    cd - >/dev/null
+
+    _wd "Exiting with rc=$_rc"
+    exit $_rc
+}
 
 #
 # public function - renew a certificate
 # param  string  fqdn of domain to renew
 function public_renew(){
-	_debuglog "start public_renew"
-	_wait_for_free_slot
-	_requiresFqdn
-	_certMustExist
-
-	_fixKeyfile
-
-	$ACME --renew -d ${CM_fqdn} $ACME_Params
-	local _rc=$?
-
-	case $_rc in
-		0)
-			_certTransfer
-			_certMatching
-			_update "renewed ${CM_fqdn}"
-			;;
-		2)
-			_wd "renew was skipped ... we need to wait a while."
-			;;
-		*)
-			_wd "Error ocured."
-			exit $_rc
-	esac
-	_debuglog "end public_renew"
+    _debuglog "start public_renew"
+    _wait_for_free_slot
+    _requiresFqdn
+    _certMustExist
+
+    _fixKeyfile
+
+    if [ $CM_force -ne 0 ]; then
+        _wd "renew was forced"
+        $ACME --force --renew -d "${CM_fqdn}" $ACME_Params
+    else
+        _wd "soft renew"
+        $ACME --renew -d "${CM_fqdn}" $ACME_Params
+    fi
+    local _rc=$?
+
+    case $_rc in
+        0)
+            _certTransfer
+            _certMatching
+            _update "renewed ${CM_fqdn}"
+            ;;
+        2)
+            _wd "renew was skipped ... we need to wait a while."
+            ;;
+        *)
+            _wd "Error ocured."
+            exit $_rc
+    esac
+    _debuglog "end public_renew"
 }
 
 #
 # public function - renew all certificates (to be used in a cronjob)
 # no params
 function public_renew-all(){
-
-	_listCertdomains | while read mydomain
-	do
-		_wd "--- renew $mydomain"
-		_setenv ${mydomain}
-		public_renew
-	done
-
+    _listCertdomains | while read mydomain
+    do
+        _wd "--- renew $mydomain"
+        _setenv ${mydomain}
+        public_renew
+    done
 }
 
 
@@ -683,105 +818,103 @@ function public_renew-all(){
 # param  string  command to verify
 # param  string  output label
 function _selftestItem(){
-	local _check=$1
-	local _label=$2
-	local _status="OK:"
-
-	eval "$_check"
-	if [ $? -ne 0 ]; then
-		_status="ERROR: the check failed for the test of -"
-		_iErrors=$_iErrors+1
-	fi
+    local _check=$1
+    local _label=$2
+    local _status="OK:"
+
+    eval "$_check"
+    if [ $? -ne 0 ]; then
+        _status="ERROR: the check failed for the test of -"
+        _iErrors=$_iErrors+1
+    fi
 
-	echo "$_status $_label"
+    echo "$_status $_label"
 }
 
 #
 # list existing certs
 # no params
 function public_selftest(){
+    typeset -i _iErrors=0
 
-	typeset -i _iErrors=0
-
-	echo
-	echo --- dependencies
-	_selftestItem "which openssl" "openssl was found"
-	_selftestItem "which curl" "curl was found"
-	echo
-
-	echo --- acme.sh client
-	_selftestItem "ls -ld ${ACME}" "${ACME} exits"
-	_selftestItem "test -x ${ACME}" "${ACME} is executable"
-	echo
-
-	echo --- acme.sh installation \(may fail in future releases of acme.sh\)
-	_selftestItem "ls -ld ~/.acme.sh" "internal acme data were found = [acme.sh --install] was done"
-	_selftestItem "test -w ~/.acme.sh/" "it is writable"
-	echo
-
-	# CSR USAGE WAS REMOVED
-	# echo --- csr template
-	# _selftestItem "ls -ld ${csrfile}"  "csr base template exists"
-	# _selftestItem "test -r ${csrfile}" "it is readable"
-	# echo
-    #
-	# echo --- output directory for csr and key
-	# _selftestItem "ls -ld ${CM_dircsr}"  "data dir for csr exists"
-	# _selftestItem "test -w ${CM_dircsr}" "it is writable"
-	# echo
-
-	echo --- output dir for centralized place of certificates
-	_selftestItem "ls -ld ${CM_diracme}"  "central output dir for certificate data exists"
-	_selftestItem "test -w ${CM_diracme}" "it is writable"
-	echo
-
-
-	echo --- logs
-	_selftestItem "ls -ld ./log/" "Logdir exists"
-	_selftestItem "test -w" "Logdir is writable"
-	test -f $logfile    &&  _selftestItem  "test -w $logfile" "Logfile $logfile is writable"
-	test -f $touchfile  && _selftestItem "test -w $touchfile" "Logfile $touchfile is writable"
-	echo
-
-	echo --- Errors: $_iErrors
-	test $_iErrors -eq 0 && echo "OK, this looks fine."
-	echo
-	exit $_iErrors
+    echo
+    echo --- dependencies
+    _selftestItem "which openssl" "openssl was found"
+    _selftestItem "which curl" "curl was found"
+    echo
+
+    echo --- acme.sh client
+    _selftestItem "ls -ld ${ACME}" "${ACME} exits"
+    _selftestItem "test -x ${ACME}" "${ACME} is executable"
+    echo
+
+    echo --- acme.sh installation \(may fail in future releases of acme.sh\)
+    _selftestItem "ls -ld ~/.acme.sh" "internal acme data were found = [acme.sh --install] was done"
+    _selftestItem "test -w ~/.acme.sh/" "it is writable"
+    echo
+
+    # CSR USAGE WAS REMOVED
+    # echo --- csr template
+    # _selftestItem "ls -ld ${csrfile}"  "csr base template exists"
+    # _selftestItem "test -r ${csrfile}" "it is readable"
+    # echo
+#
+    # echo --- output directory for csr and key
+    # _selftestItem "ls -ld ${CM_dircsr}"  "data dir for csr exists"
+    # _selftestItem "test -w ${CM_dircsr}" "it is writable"
+    # echo
+
+    echo --- output dir for centralized place of certificates
+    _selftestItem "ls -ld ${CM_diracme}"  "central output dir for certificate data exists"
+    _selftestItem "test -w ${CM_diracme}" "it is writable"
+    echo
+
+    echo --- logs
+    _selftestItem "ls -ld ./log/" "Logdir exists"
+    _selftestItem "test -w" "Logdir is writable"
+    test -f $logfile    &&  _selftestItem  "test -w $logfile" "Logfile $logfile is writable"
+    test -f $touchfile  && _selftestItem "test -w $touchfile" "Logfile $touchfile is writable"
+    echo
+
+    echo --- Errors: $_iErrors
+    test $_iErrors -eq 0 && echo "OK, this looks fine."
+    echo
+    exit $_iErrors
 }
 
 #
 # list existing certs
 # no params
 function public_show(){
-	_requiresFqdn
-	_certMustExist
-
-	# CSR USAGE WAS REMOVED
-	# ls -l ${CM_filecsr} ${CM_dircerts}/*
-	ls -l ${CM_dircerts}/*
-	_certMatching
-
-	# CSR USAGE WAS REMOVED
-	# echo $line
-	# echo CSR $CM_filecsr
-	# openssl req -noout -text -in $CM_filecsr | grep -E "(Subject:|DNS:)" | sed "s#^\ *##g"
-
-	for myfile in ${CM_outfile_cert} ${CM_outfile_haproxy}
-	do
-        echo $line
-        echo Cert ${myfile}
-        # openssl x509 -noout -text -in ${CM_outfile_cert}
-        openssl x509 -noout -text -in ${myfile} | grep -E "(Issuer:|Subject:|Not\ |DNS:)"| sed "s#^\ *##g"
-    done
+    _requiresFqdn
+    _certMustExist
+
+    # CSR USAGE WAS REMOVED
+    # ls -l ${CM_filecsr} ${CM_dircerts}/*
+    ls -l ${CM_dircerts}/*
+    _certMatching
+
+    # CSR USAGE WAS REMOVED
+    # echo $line
+    # echo CSR $CM_filecsr
+    # openssl req -noout -text -in $CM_filecsr | grep -E "(Subject:|DNS:)" | sed "s#^\ *##g"
+
+    for myfile in ${CM_outfile_cert} ${CM_outfile_haproxy}
+    do
+    echo $line
+    echo Cert ${myfile}
+    # openssl x509 -noout -text -in ${CM_outfile_cert}
+    openssl x509 -noout -text -in ${myfile} | grep -E "(Issuer:|Subject:|Not\ |DNS:)"| sed "s#^\ *##g"
+done
 }
 
 # Transfer cert from acme.sh internal cache to our output dir again
 function public_transfer(){
-	_wait_for_free_slot
-	_requiresFqdn
-	_certExists
+    _wait_for_free_slot
+    _requiresFqdn
+    _certExists
 
-	_certTransfer
+    _certTransfer
 }
 
 # ----------------------------------------------------------------------
@@ -792,37 +925,78 @@ function public_transfer(){
 
 cd $( dirname $0 )
 
-if [ "$1" = "--trace" ]; then
-	tracelog="$logdir/trace__$2__$3__`date +%Y-%m-%d__%H-%M-%S`.log"
-	exec  > >(tee -a ${tracelog}    )
-	exec 2> >(tee -a ${tracelog} >&2)
-	echo "TRACELOG was triggered."
-	echo "TIME   : $(date)"
-	echo "COMMAND: $0 $*"
-	echo "LOG    : $tracelog"
-
-	# set -vx
-	shift 1
-fi
-
 cat <<ENDOFHEADER
 $line
 
 
-             	- - - ---===>>> CERT MANAGER <<<===--- - - -
+                - - - ---===>>> CERT MANAGER - v$_version <<<===--- - - -
 
 $line
 
 ENDOFHEADER
 
+
+while [[ "$#" -gt 0 ]]; do case $1 in
+
+    -a|--alias)   
+        cd ..
+        CM_webroot="$( pwd )/alias-dir"
+        cd -
+        echo "INFO: directory for challenges is [$CM_webroot]"
+        shift
+        ;;
+
+    -f|--force)
+        echo "INFO: enable --force (for renewal)"
+        CM_force=1
+        exit 0
+        ;;
+
+    -h|--help)
+        show_help
+        exit 0
+        ;;
+
+    -t|--trace)     
+        tracelog="$logdir/trace__$2__$3__$(date +%Y-%m-%d__%H-%M-%S).log"
+        exec  > >(tee -a "${tracelog}"    )
+        exec 2> >(tee -a "${tracelog}" >&2)
+        echo "TRACELOG was triggered."
+        echo "TIME   : $(date)"
+        echo "COMMAND: $0 $*"
+        echo "LOG    : $tracelog"
+        # set -vx
+        shift 1
+        ;;
+
+    -v|--verbose) 
+        CM_showdebug=1
+        shift 1
+        ;;
+
+    -w|--webroot) 
+        CM_webroot="$2"
+        echo "INFO: directory for challenges is [$CM_webroot]"
+        # ACME="sudo $ACME"
+        # shift
+        shift
+        ;;
+
+    *) if grep "^-" <<< "$1" >/dev/null ; then
+        echo; _we "Unknown parameter: $1"; show_help; exit 2
+       fi
+       break;
+       ;;
+esac; done
+
 which openssl >/dev/null || exit 1
 
 . ./inc_config.sh
 if [ $? -ne 0 ]; then
-	echo "ERROR: loading the config failed."
-	echo "Copy the inc_config.sh.dist to inc_config.sh and make your settings in it."
-	echo
-	exit 1
+    _we "Loading the config failed."
+    echo "Copy the inc_config.sh.dist to inc_config.sh and make your settings in it."
+    echo
+    exit 1
 fi
 
 _testUser
@@ -831,103 +1005,34 @@ _testStaging
 test -z "${CM_diracme}" && CM_diracme=./certs
 test -z "${CM_dircsr}"  && CM_dircsr=./csr
 
-grep "function\ public_$1" $( basename $0 ) >/dev/null 
+grep "function public_$1" "$( basename "$0" )" >/dev/null 
 if [ $# -gt 0 -a $? -eq 0 ]; then
-	# _wd $*
-	action=$1
-	CM_fqdn=$2
-	shift 2
+    # _wd $*
+    action=$1
+    CM_fqdn=$2
+    shift 2
 
-	_testFqdncount $CM_fqdn $*
+    _testFqdncount "$CM_fqdn" $*
 
-	test -z "${ACME}" && ACME=$( which acme.sh )
-	if [ ! -x "${ACME}" ]; then
-		echo "ERROR: acme.sh not found. You need to install acme.sh client and configure it in inc_config.sh."
-		exit 1
-	fi
+    test -z "${ACME}" && ACME=$( which acme.sh )
+    if [ ! -x "${ACME}" ]; then
+        _we "acme.sh not found. You need to install acme.sh client and configure it in inc_config.sh."
+        exit 1
+    fi
 
-	_setenv $CM_fqdn
+    _setenv "$CM_fqdn"
 
-	_wd "A C T I O N -->> $action <<--"
-	_debuglog ">>> START public_$action $CM_fqdn $*"
-	eval "public_$action $*"
-	_debuglog ">>> DONE public_$action $CM_fqdn $*"
+    _wd "A C T I O N -->> $action <<--"
+    _debuglog ">>> START public_$action $CM_fqdn $*"
+    eval "public_$action $*"
+    _debuglog ">>> DONE public_$action $CM_fqdn $*"
 
 else
-	self=$( basename $0 )
-	cat <<EOF
-
-HELP
-
-The basic syntax is
-$self [--trace] ACTION [FQDN] [ALIAS_1 [.. ALIAS_N]]
-
-The ACTIONs for SINGLE certificate handlings are:
-
-        add FQDN [.. FQDN-N] 
-                create new certificate
-                The first FQDN is a hostname to generate the certificate for. 
-                Following multiple hostnames will be used as DNS aliases in the 
-                same certificate.
-                It updates files in ${CM_diracme}
-
-        ensure FQDN [.. FQDN-N] 
-                It ensures that a certificate with given aliases exists and is up to date.
-                This param is for simple usage in automation tools like Ansible or Puppet.
-                It is required to add all aliases as parameters what is unhandy for
-                direct usage on cli.
-
-                If the cert does not exist it will be created (see "add").
-                If fqdn and aliases are the same like in the certificate it performs a renew.
-                If fqdn and aliases differ:
-                - the current certificate will be rejected + deleted (see "delete")
-                - a new certificate will be added ()
-
-        delete FQDN
-                delete all files of a given certificate
-
-        renew FQDN
-                renew (an already added) certificate
-                and update files in ${CM_diracme}
-
-        show FQDN
-                show place of certificate data and show basic certificate data
-                (issuer, subject, aliases, ending date)
-
-        transfer FQDN
-                Transfer cert from acme.sh internal cache to our output dir again.
-                It is done during add or renew. With transfer command you can repeat it.
-
-ACTIONs for ALL certs
-
-        list
-                list all certificates including creation and renew date
-
-        list-old
-                list all certificates older 65 and older 90 days and exit.
-				Exitcodes:
-				  0 - all certs are up to date.
-				  1 - certificates to renew were found
-				  2 - outdatedt certificates were found
-
-        renew-all
-                renew all certificates (fast mode - without --force)
-                and update files in ${CM_diracme}
-                It is useful for a cronjob.
-
-other ACTIONs
-
-        selftest
-                check of health with current setup and requirements.
-                This command is helpful for initial setups.
-
-OPTIONS
-        --trace (it must be the 1st parameter) 
-                the output additionally will be written into a tracelog file 
-                below $logdir.
-
-EOF
+    _we "No valid action was specified"
+    show_help
 fi
 
 echo
 _testStaging
+
+# ----------------------------------------------------------------------
diff --git a/docs/30_Usage.md b/docs/30_Usage.md
index e998df73f32eb3eb9d25c9a85a96c14588dc955d..f172c21e0309c5a7c192cf62abe7865442695c6d 100644
--- a/docs/30_Usage.md
+++ b/docs/30_Usage.md
@@ -6,94 +6,113 @@ Verify a new setup (or changes in the config) with `./cm.sh selftest`.
 
 ## Show help
 
-Without any parameter it shows a help.
+Without -h or --help it shows a help.
 
 ```text
 
-./cm.sh 
+./cm.sh -h
 _______________________________________________________________________________
 
 
-                - - - ---===>>> CERT MANAGER <<<===--- - - -
+                - - - ---===>>> CERT MANAGER - v2025-01-08 <<<===--- - - -
 
 _______________________________________________________________________________
 
-DEBUG: Using Let's Encrypt STAGE environment ...
-DEBUG: You can test and mess around. Do not use certs in production.
-
 
 HELP
 
-The basic syntax is
-cm.sh [--trace] ACTION [FQDN] [ALIAS_1 [.. ALIAS_N]]
+Wrapper script for acme.sh to handle certificates.
+For automation you should use the "ensure" action that detects if a 
+certificate must be created, renewed or re-created.
 
-The ACTIONs for SINGLE certificate handlings are:
+📄 Source: <https://git-repo.iml.unibe.ch/iml-open-source/iml-certman>
+📜 License: GNU GPL 3.0
+📗 Docs: <https://os-docs.iml.unibe.ch/iml-certman/>
 
-        add FQDN [.. FQDN-N] 
-                create new certificate
-                The first FQDN is a hostname to generate the certificate for. 
-                Following multiple hostnames will be used as DNS aliases in the 
-                same certificate.
-                It updates files in ./certs
+SYNTAX:
 
-        ensure FQDN [.. FQDN-N] 
-                It ensures that a certificate with given aliases exists and is up to date.
-                This param is for simple usage in automation tools like Ansible or Puppet.
-                It is required to add all aliases as parameters what is unhandy for
-                direct usage on cli.
+    dev_cm.sh [OPTIONS] ACTION <FQDN> [<ALIASES>]
 
-                If the cert does not exist it will be created (see "add").
-                If fqdn and aliases are the same like in the certificate it performs a renew.
-                If fqdn and aliases differ:
-                - the current certificate will be rejected + deleted (see "delete")
-                - a new certificate will be added ()
+OPTIONS:
 
-        delete FQDN
-                delete all files of a given certificate
+    -a|--alias
+        Use http challenge with existing http server on port 80
+        Challenge file will be written into ../alias-dir/
 
-        renew FQDN
-                renew (an already added) certificate
-                and update files in ./certs
+    -f|--force
+        Force renew of certificate even if it is not due yet.
+        Use it carefully - remember the execution limits on Let's Encrypt.
 
-        show FQDN
-                show place of certificate data and show basic certificate data
-                (issuer, subject, aliases, ending date)
+    -t|--trace
+        the output additionally will be written into a tracelog file 
+        below ./log.
 
-        transfer FQDN
-                Transfer cert from acme.sh internal cache to our output dir again.
-                It is done during add or renew. With transfer command you can repeat it.
+    -v|--verbose
+        show debug infos on console.
+        Remark: for permanent usage set CM_showdebug=1 in inc_config.sh
 
-ACTIONs for ALL certs
+    -w|--webroot <DIR>
+        Use http challenge with existing http server on port 80
+        Challenge file will be written into given directory
 
-        list
-                list all certificates including creation and renew date
+The ACTIONs for SINGLE certificate handlings are:
 
-        list-old
-                list all certificates older 65 and older 90 days and exit.
-                Exitcodes:
-                  0 - all certs are up to date.
-                  1 - certificates to renew were found
-                  2 - outdatedt certificates were found
+    add <FQDN> [.. <FQDN-N>] 
+        create new certificate
+        The first FQDN is a hostname to generate the certificate for. 
+        Following multiple hostnames will be used as DNS aliases in the 
+        same certificate.
+        It updates files in 
 
-        renew-all
-                renew all certificates (fast mode - without --force)
-                and update files in ./certs
-                It is useful for a cronjob.
+    ensure <FQDN> [.. <FQDN-N>] 
+        It ensures that a certificate with given aliases exists and is up to date.
+        This param is for simple usage in automation tools like Ansible or Puppet.
+        It is required to add all aliases as parameters what is unhandy for
+        direct usage on cli.
 
-other ACTIONs
+        If the cert does not exist it will be created (see "add").
+        If fqdn and aliases are the same like in the certificate it performs a renew.
+        If fqdn and aliases differ:
+        - the current certificate will be rejected + deleted (see "delete")
+        - a new certificate will be added ()
+
+    delete <FQDN>
+        delete all files of a given certificate
+
+    renew <FQDN>
+        renew (an already added) certificate
+        and update files in 
 
-        selftest
-                check of health with current setup and requirements.
-                This command is helpful for initial setups.
+    show <FQDN>
+        show place of certificate data and show basic certificate data
+        (issuer, subject, aliases, ending date)
 
-OPTIONS
-        --trace (it must be the 1st parameter) 
-                the output additionally will be written into a tracelog file 
-                below ./log.
+    transfer <FQDN>
+        Transfer cert from acme.sh internal cache to our output dir again.
+        It is done during add or renew. With transfer command you can repeat it.
 
+ACTIONs for ALL certs
+
+    list
+        list all certificates including creation and renew date
+
+    list-old
+        list all certificates older 65 and older 90 days and exit.
+        Exitcodes:
+            0 - all certs are up to date.
+            1 - certificates to renew were found
+            2 - outdatedt certificates were found
+
+    renew-all
+        renew all certificates (fast mode - without --force)
+        and update files in 
+        It is useful for a cronjob.
+
+other ACTIONs
 
-DEBUG: Using Let's Encrypt STAGE environment ...
-DEBUG: You can test and mess around. Do not use certs in production.
+    selftest
+        check of health with current setup and requirements.
+        This command is helpful for initial setups.
 
 ```
 
diff --git a/docs/style.css b/docs/style.css
index 18463c799d9cd101d8f9b27a282128cde4494d99..90a0bc938f147608a1d4fd11952e4825f076862c 100644
--- a/docs/style.css
+++ b/docs/style.css
@@ -1,52 +1,59 @@
 /*
     override css elements of daux.io blue theme
-    version 2022-11-30
+    version 2024-10-31
 */
 :root {
     /* Axels Overrides */
-    --color-text: #222;
-    --link-color: #822;
-    --brand-color: var(--color-secondary);
+    --color-text: #234;
+    --link-color: #228;
+    --brand-color: var(--color-text);
     --brand-background: var(--body-background);
+    --code-tag-background-color: #f0f3f3;
+    --code-tag-border-color: #dee;
+    --code-tag-box-shadow: none;
     --hr-color: none;
+    --pager-background-color: #f8fafa;
+    --pager-border-color: none;
     --search-field-background: none;
     --search-field-border-color: none;
     --sidebar-background: var(--body-background);
     --sidebar-border-color: none;
-    --sidebar-link-active-background: #e8f4f6;
-    --sidebar-link-active-background: #eee;
+    --sidebar-link-active-background: #f0f4f6;
+    --toc--inner-border-color: none;
     /* Axels custom values */
-    --axel_bg-toc: var(--body-background);
+    --axel_bg-toc: #f8fafa;
     --axel_bg-toc-head: #f8f8f8;
     --axel_brand-background: none;
     --axel_brand-pre-background: rgb(255, 0, 51);
-    ;
     --axel_brand-pre-background-hover: rgb(255, 0, 51);
-    ;
     --axel_h1_header: none;
-    --axel_h1: #345;
+    --axel_h1: #111;
     --axel_h1-bg: none;
     --axel_h1-bottom: 3px solid none;
-    --axel_h2: #156;
-    --axel_h2-bg: #f8fafb;
-    --axel_h2-bottom: 2px solid #467;
+    --axel_h2: #222;
+    --axel_h2-bg: none;
+    --axel_h2-bottom: 0px solid #467;
     --axel_h2-hero-bottom: 2px solid #912;
-    --axel_h3: #278;
-    --axel_h3-bottom: 1px solid #ddd;
-    --axel_hero_bg: #f8f8f8;
+    --axel_h3: #333;
+    --axel_h3-bottom: 0px solid #ddd;
+    --axel_h4: #666;
+    --axel_h5: #888;
+    --axel_hero_bg: #faf8f6;
+    --axel_img-border: 2px dashed #ccc;
     --axel_nav-bg: #fcfcfc;
     --axel_nav-buttomborder: #ddd;
-    --axel_pre-background: #f8f8f8;
-    --axel-th-background: #d0e0e8;
+    --axel_pre-background: #faf8f6;
+    --axel-th-background: #e0e4e8;
     --axel-article-nav-border-top: 0px dotted #ddd;
 }
 
 .dark {
     /* Axels Overrides */
     --color-text: #c0c0c0;
-    --link-color: #b44;
+    --link-color: #88e;
     --brand-color: var(--color-text);
     --brand-background: var(--body-background);
+    --body-background: #101418;
     --hr-color: none;
     --code-tag-background-color_: #bcc;
     --search-field-background: none;
@@ -54,6 +61,7 @@
     --sidebar-background: var(--body-background);
     --sidebar-border-color: none;
     --sidebar-link-active-background: #333;
+    --sidebar-link-color: var(--link-color);
     /* Axels custom values */
     --axel_bg-toc: var(--body-background);
     --axel_bg-toc-head: #333;
@@ -63,16 +71,19 @@
     --axel_brand-pre-background-hover: rgb(255, 0, 51);
     ;
     --axel_h1_header: none;
-    --axel_h1: #777;
+    --axel_h1: #578;
     --axel_h1-bg: none;
     --axel_h1-bottom: none;
     --axel_h2: #467;
-    --axel_h2-bg: #202020;
-    --axel_h2-bottom: 2px solid #256;
+    --axel_h2-bg: none;
+    --axel_h2-bottom: 0px solid #256;
     --axel_h2-hero-bottom: 2px solid #712;
     --axel_h3: #589;
-    --axel_h3-bottom: 1px solid #333;
+    --axel_h3-bottom: 0px solid #333;
+    --axel_h4: #478;
+    --axel_h5: #278;
     --axel_hero_bg: #242424;
+    --axel_img-border: 2px dashed #555;
     --axel_nav-bg: #242424;
     --axel_nav-buttomborder: #555;
     --axel_pre-background: #bcc;
@@ -117,7 +128,25 @@ a.Brand {
 
 /* ---------- page content ---------- */
 .s-content {
-    padding-top: 1em;
+    padding-top: 6em;
+}
+
+/**
+h1::before{color: #aaa;content: 'h1: ';}
+h2::before{color: #aaa;content: 'h2: ';}
+h3::before{color: #aaa;content: 'h3: ';}
+h4::before{color: #aaa;content: 'h4: ';}
+h5::before{color: #aaa;content: 'h5: ';}
+h6::before{color: #aaa;content: 'h6: ';}
+*/
+h2::before{color: #888;content: ': : ';}
+h3::before{color: #ccc;content: '> ';}
+h4::before{color: #ccc;content: '_ ';}
+
+.s-content h1::before{
+    color: #f00;
+    content: 'FEHLER: Keine Überschrift 1 in einer Markdown-Datei für Daux verwenden! Mit H2 beginnen!';
+    content: '!! h1 !! ';
 }
 
 .s-content h1 {
@@ -133,20 +162,32 @@ a.Brand {
 .s-content h2 {
     background: var(--axel_h2-bg);
     color: var(--axel_h2);
-    font-size: 180%;
+    font-size: 190%;
     font-weight: bold;
     margin-top: 4em;
     border-bottom: var(--axel_h2-bottom);
 }
 
-h1:first-of-type {
+.Page__header > h1:first-of-type {
     margin-top: 0em;
+    margin-left: -1em;
+    padding-left: 1em;
+    position: fixed;
+    min-width: 100%;
+    background: var(--body-background);
+    box-shadow: 0 2em 1em var(--body-background);
 }
 
 h2:first-of-type {
     margin-top: 0em;
 }
 
+img{
+    border: var(--axel_img-border);
+    border-radius: 1.5em;
+    padding: 0.7em;
+}
+
 .s-content h3 {
     background: var(--axel_h3-bg);
     color: var(--axel_h3);
@@ -156,16 +197,37 @@ h2:first-of-type {
     border-bottom: var(--axel_h3-bottom);
 }
 
-.s-content h4 {
-    margin: 0;
-    font-size: 100%;
+
+.s-content > h4 {
+    color: var(--axel_h4);
+    font-size: 140%;
+    font-weight: bold;
+    margin: 2em 0;
+}
+
+.s-content > h5 {
+    color: var(--axel_h5);
+    font-size: 135%;
+    font-weight: bold;
+    margin: 2em 0;
+}
+
+.s-content .TableOfContentsContainer h4 {
+    margin: 1em 0;
+    font-size: 110%;
     text-align: center;
-    background-color: rgba(0, 0, 0, 0.05);
+    background-color: rgba(0, 0, 0, 0.1);
     padding: 0.3em;
+    font-weight: bold;
+    font-family: Arial;
+}
+ul.TableOfContents a{
+    color: var(--color-text);
 }
-
 .s-content pre {
     background: var(--axel_pre-background);
+    border-radius: 0.5em;
+    padding: 1rem;
 }
 
 /* FIX smaller fnt size in tables */
@@ -217,13 +279,6 @@ div.hero h2 {
 }
 
 /* ---------- TOC ---------- */
-@media(min-width:1700px) {
-    .TableOfContentsContainer {
-        position: fixed;
-        right: 2em;
-        top: 1em;
-    }
-}
 
 .TableOfContentsContainer {
     background-color: var(--axel_bg-toc);
@@ -233,14 +288,18 @@ div.hero h2 {
 .s-content .TableOfContentsContainer h4 {
     background-color: var(--axel_bg-toc-head);
     border-top-left-radius: 1em;
+    border-bottom: 2px solid var(--axel_bg-toc-bottom-border);
     font-size: 1.1em;
     margin: 0;
-    padding: 0;
+    padding: 0.3em;
+    display: none;
 }
 
 .TableOfContentsContainer__content {
-    border-width: 1px;
+    border-width: 0px;
     font-size: 0.5em;
+    height: inherit; 
+    overflow: auto;
 }
 
 ul.TableOfContents ul {
@@ -248,20 +307,30 @@ ul.TableOfContents ul {
     padding-left: 1em;
 }
 
+.TableOfContents a:hover{
+    text-decoration: underline;
+}
+
+@media(min-width:1700px) {
+    .TableOfContentsContainer {
+        background: none;
+        position: fixed;
+        right: 2em;
+        top: 4em;
+        height: 90%;
+    }
+}
+
 /* ----- Icons on links --- */
 
 .EditOn a::before{
     content: '✏️ ';
 }
 
-.Links a[href^="https://github.com/"]::before {
-    content: '🌐 ';
-}
-
-.Links a[href^="https://git-repo.iml.unibe.ch/"]::before {
+.Links a::before {
     content: '🌐 ';
 }
 
 .Links a[href^="https://os-docs.iml.unibe.ch"]::before {
     content: '📗 ';
-}
\ No newline at end of file
+}