diff --git a/.gitignore b/.gitignore
index 0813e884..0aa528f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,4 +33,7 @@ pids
 
 # Coverage directory used by tools like istanbul
 coverage
-*.lcov
\ No newline at end of file
+*.lcov
+
+# vscode
+.vscode/launch.json
\ No newline at end of file
diff --git a/.golangci.yml b/.golangci.yml
index 6c97e81a..f580e5f8 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -21,7 +21,6 @@ linters:
     - bidichk          # Checks for dangerous unicode character sequences
     # - bodyclose        # checks whether HTTP response body is closed successfully
     # - contextcheck     # check the function whether use a non-inherited context
-    - deadcode         # Finds unused code
     - decorder         # check declaration order and count of types, constants, variables and functions
     # - depguard         # Go linter that checks if package imports are in a list of acceptable packages
     - dogsled          # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
@@ -74,7 +73,6 @@ linters:
     - unconvert        # Remove unnecessary type conversions
     - unparam          # Reports unused function parameters
     - unused           # Checks Go code for unused constants, variables, functions and types
-    - varcheck         # Finds unused global variables and constants
     # - wastedassign     # wastedassign finds wasted assignment statements
     - whitespace       # Tool for detection of leading and trailing whitespace
     - makezero         # Finds slice declarations with non-zero initial length
diff --git a/cmd/defaultConfig.go b/cmd/defaultConfig.go
index 8730b0ce..6036e5c2 100644
--- a/cmd/defaultConfig.go
+++ b/cmd/defaultConfig.go
@@ -43,7 +43,7 @@ func resolveDefaultConfig(configPath string) error {
 		return nil
 	}
 	configDirectoryPath := filepath.Dir(configPath)
-	cfg := config.DefaultConfig(configDirectoryPath + "/www")
+	cfg := config.DefaultConfig(configDirectoryPath)
 	if err := os.WriteFile(configPath, []byte(cfg.String()), 0o600); err != nil {
 		return fmt.Errorf("cannot write default config: %w", err)
 	}
diff --git a/cmd/generateSelfSigned.go b/cmd/generateSelfSigned.go
new file mode 100644
index 00000000..3bad27e1
--- /dev/null
+++ b/cmd/generateSelfSigned.go
@@ -0,0 +1,172 @@
+package main
+
+import (
+	"context"
+	"crypto/ecdsa"
+	"crypto/elliptic"
+	"crypto/rand"
+	"crypto/tls"
+	"crypto/x509"
+	"crypto/x509/pkix"
+	"encoding/pem"
+	"fmt"
+	"math/big"
+	"net"
+	"os"
+	"path"
+	"strings"
+	"time"
+
+	"github.com/apex/log"
+	externalip "github.com/glendc/go-external-ip"
+)
+
+func getExternalIP() (net.IP, error) {
+	consensus := externalip.DefaultConsensus(&externalip.ConsensusConfig{
+		Timeout: time.Second * 3,
+	}, nil)
+	return consensus.ExternalIP()
+}
+
+func getExternalDomains(ip net.IP) ([]string, error) {
+	ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
+	defer cancel()
+	dns, err := net.DefaultResolver.LookupAddr(ctx, ip.String())
+	if err != nil {
+		return nil, err
+	}
+	// remove trailing dot
+	for i := range dns {
+		dns[i] = strings.TrimSuffix(dns[i], ".")
+	}
+	return dns, nil
+}
+
+func setupIPsFromInterface(template *x509.Certificate, iface net.Interface) {
+	if iface.Flags&net.FlagLoopback != 0 {
+		return
+	}
+	if iface.Flags&net.FlagUp == 0 {
+		return
+	}
+	addrs, err := iface.Addrs()
+	if err != nil {
+		log.Warnf("cannot get network interface(%v) addresses: %v", iface.Name, err)
+		return
+	}
+	for _, addr := range addrs {
+		if ip, _, err3 := net.ParseCIDR(addr.String()); err3 == nil && !ip.IsUnspecified() {
+			template.IPAddresses = append(template.IPAddresses, ip)
+		} else {
+			log.Warnf("cannot parse network interface(%v) address(%v): %v", iface.Name, addr.String(), err3)
+		}
+	}
+}
+
+func setupIPsFromInterfaces(template *x509.Certificate) {
+	ifaces, err := net.Interfaces()
+	if err != nil {
+		log.Warnf("cannot get network interfaces: %v", err)
+		return
+	}
+	for _, iface := range ifaces {
+		setupIPsFromInterface(template, iface)
+	}
+}
+
+func setupIPAndDomains(template *x509.Certificate) {
+	template.DNSNames = []string{"localhost"}
+	template.IPAddresses = []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")}
+	if ip, err := getExternalIP(); err != nil {
+		log.Warnf("cannot get external IP: %v", err)
+	} else {
+		template.IPAddresses = append(template.IPAddresses, ip)
+		domains, err := getExternalDomains(ip)
+		if err != nil {
+			log.Warnf("cannot get external domains: %v", err)
+		} else {
+			template.DNSNames = append(template.DNSNames, domains...)
+		}
+	}
+	setupIPsFromInterfaces(template)
+}
+
+const cn = "self-signed-certificate"
+
+func checkSelfSignedCertificate(certFile, keyFile string) bool {
+	crt, err := tls.LoadX509KeyPair(certFile, keyFile)
+	if err != nil {
+		return false
+	}
+	cert, err := x509.ParseCertificate(crt.Certificate[0])
+	if err != nil {
+		return false
+	}
+	if cert.Subject.CommonName != cn {
+		// it is not self signed certificate
+		return true
+	}
+	now := time.Now()
+	if now.Before(cert.NotBefore) || now.After(cert.NotAfter) {
+		return false
+	}
+	return true
+}
+
+func generateSelfSigned(certFile, keyFile string) error {
+	err := os.MkdirAll(path.Dir(certFile), 0o700)
+	if err != nil {
+		return fmt.Errorf("cannot create directory(%v) for certificate: %w", path.Dir(certFile), err)
+	}
+	err = os.MkdirAll(path.Dir(keyFile), 0o700)
+	if err != nil {
+		return fmt.Errorf("cannot create directory(%v) for key: %w", path.Dir(keyFile), err)
+	}
+	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+	if err != nil {
+		return fmt.Errorf("cannot generate key: %w", err)
+	}
+	template := x509.Certificate{
+		SerialNumber: big.NewInt(1),
+		Subject: pkix.Name{
+			Organization: []string{"plgd.dev"},
+			CommonName:   cn,
+		},
+		NotBefore: time.Now(),
+		NotAfter:  time.Now().Add(time.Hour * 24 * 365),
+
+		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
+		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
+		BasicConstraintsValid: true,
+		IsCA:                  true,
+	}
+	setupIPAndDomains(&template)
+
+	derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
+	if err != nil {
+		return fmt.Errorf("cannot create certificate: %w", err)
+	}
+
+	derKeyBytes, err := x509.MarshalPKCS8PrivateKey(priv)
+	if err != nil {
+		return fmt.Errorf("cannot marshal private key: %w", err)
+	}
+
+	err = os.WriteFile(certFile, pem.EncodeToMemory(&pem.Block{
+		Type:  "CERTIFICATE",
+		Bytes: derBytes,
+	}), 0o600)
+	if err != nil {
+		return fmt.Errorf("cannot write certificate to file(%v): %w", certFile, err)
+	}
+
+	err = os.WriteFile(keyFile, pem.EncodeToMemory(&pem.Block{
+		Type:  "PRIVATE KEY",
+		Bytes: derKeyBytes,
+	}), 0o600)
+	if err != nil {
+		return fmt.Errorf("cannot write key to file(%v): %w", keyFile, err)
+	}
+
+	return nil
+}
diff --git a/cmd/main.go b/cmd/main.go
index 574f39cc..b29451f4 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -37,7 +37,7 @@ var (
 	ReleaseURL = "unknown url"
 )
 
-func main() {
+func loadConfig() config.Config {
 	var opts struct {
 		Version    bool   `short:"v" long:"version" description:"version"`
 		ConfigPath string `long:"config" description:"yaml config file path"`
@@ -45,34 +45,49 @@ func main() {
 	_, _ = flags.NewParser(&opts, flags.Default|flags.IgnoreUnknown).Parse()
 	if opts.Version {
 		fmt.Println(Version)
-		return
+		os.Exit(0)
 	}
 	if err := resolveDefaultConfig(opts.ConfigPath); err != nil {
 		log.Errorf("cannot create default config: %v", err)
-		return
+		os.Exit(1)
 	}
 	// parse line arguments again because resolveDefaultConfig can set config path
 	_, _ = flags.NewParser(&opts, flags.Default|flags.IgnoreUnknown).Parse()
 	cfg, err := config.New(opts.ConfigPath)
 	if err != nil {
 		log.Errorf("cannot load config: %v", err)
-		return
+		os.Exit(1)
 	}
 	if _, err = os.Stat(cfg.APIs.HTTP.UI.Directory); cfg.APIs.HTTP.UI.Enabled && err != nil {
 		if err = extractUI(cfg.APIs.HTTP.UI.Directory); err != nil {
 			log.Errorf("cannot extract UI: %v", err)
+			os.Exit(1)
+		}
+	}
+	if cfg.APIs.HTTP.Enabled && cfg.APIs.HTTP.TLS.Enabled && !checkSelfSignedCertificate(cfg.APIs.HTTP.TLS.CertFile, cfg.APIs.HTTP.TLS.KeyFile) {
+		if err = generateSelfSigned(cfg.APIs.HTTP.TLS.CertFile, cfg.APIs.HTTP.TLS.KeyFile); err != nil {
+			log.Errorf("cannot generate self signed certificate for HTTP: %v", err)
+			os.Exit(1)
+		}
+	}
+	if cfg.APIs.GRPC.Enabled && cfg.APIs.GRPC.TLS.Enabled && !checkSelfSignedCertificate(cfg.APIs.GRPC.TLS.CertFile, cfg.APIs.GRPC.TLS.KeyFile) {
+		if err = generateSelfSigned(cfg.APIs.GRPC.TLS.CertFile, cfg.APIs.GRPC.TLS.KeyFile); err != nil {
+			log.Errorf("cannot generate self signed certificate for GRPC: %v", err)
+			os.Exit(1)
 		}
 	}
+	return cfg
+}
+
+func main() {
+	cfg := loadConfig()
 	logger := log.NewLogger(cfg.Log)
 	log.Set(logger)
 	fileWatcher, err := fsnotify.NewWatcher(logger)
 	if err != nil {
 		log.Errorf("cannot create file fileWatcher: %v", err)
-		return
+		os.Exit(1)
 	}
-	defer func() {
-		_ = fileWatcher.Close()
-	}()
 	log.Debugf("version: %v, buildDate: %v, buildRevision %v", Version, BuildDate, CommitHash)
 	log.Debugf("config:\n%v", cfg.String())
 	info := grpc.ServiceInformation{
@@ -86,11 +101,11 @@ func main() {
 	s, err := service.New(context.Background(), cfg, &info, fileWatcher, logger)
 	if err != nil {
 		log.Errorf("cannot create service: %v", err)
-		return
+		os.Exit(1)
 	}
 	err = s.Serve()
 	if err != nil {
 		log.Errorf("cannot serve service: %v", err)
-		return
+		os.Exit(1)
 	}
 }
diff --git a/config.yaml b/config.yaml
index 5bd50a4b..fa642bb6 100644
--- a/config.yaml
+++ b/config.yaml
@@ -16,9 +16,9 @@ apis:
     idleTimeout: 30s
     tls:
       enabled: false
-      caPool: /certs/ca.pem
-      keyFile: /certs/key.pem
-      certFile: /certs/crt.pem
+      caPool: certs/ca.pem
+      keyFile: certs/key.pem
+      certFile: certs/crt.pem
       clientCertificateRequired: true
     cors:
       allowedOrigins:
@@ -59,9 +59,9 @@ apis:
       timeout: 0s
     tls:
       enabled: false
-      caPool: /certs/ca.pem
-      keyFile: /certs/key.pem
-      certFile: /certs/crt.pem
+      caPool: certs/ca.pem
+      keyFile: certs/key.pem
+      certFile: certs/crt.pem
       clientCertificateRequired: true
 clients:
   device:
@@ -77,9 +77,9 @@ clients:
           - justWorks
         manufacturerCertificate:
           tls:
-            caPool: /certs/mfg_ca.pem
-            keyFile: /certs/mfg_key.pem
-            certFile: /certs/mfg_crt.pem
+            caPool: certs/mfg_ca.pem
+            keyFile: certs/mfg_key.pem
+            certFile: certs/mfg_crt.pem
       tls:
         authentication: preSharedKey
         preSharedKey:
diff --git a/go.mod b/go.mod
index 73be4ba4..dc0b2457 100644
--- a/go.mod
+++ b/go.mod
@@ -3,9 +3,11 @@ module github.com/plgd-dev/client-application
 go 1.18
 
 require (
-	cloud.google.com/go v0.110.3
+	cloud.google.com/go v0.110.4
+	github.com/apex/log v1.9.0
 	github.com/favadi/protoc-go-inject-tag v1.4.0
 	github.com/fullstorydev/grpchan v1.1.1
+	github.com/glendc/go-external-ip v0.1.0
 	github.com/golang-jwt/jwt/v4 v4.5.0
 	github.com/google/uuid v1.3.0
 	github.com/goreleaser/goreleaser v1.9.2
@@ -16,8 +18,8 @@ require (
 	github.com/jessevdk/go-flags v1.5.0
 	github.com/lestrrat-go/jwx v1.2.26
 	github.com/pion/dtls/v2 v2.2.7
-	github.com/plgd-dev/device/v2 v2.2.1-0.20230602173355-efc3a81993c3
-	github.com/plgd-dev/go-coap/v3 v3.1.3
+	github.com/plgd-dev/device/v2 v2.2.1-0.20230803141537-60c77f6f4864
+	github.com/plgd-dev/go-coap/v3 v3.1.4-0.20230802114331-351cd00bab2d
 	github.com/plgd-dev/hub/v2 v2.7.19
 	github.com/plgd-dev/kit/v2 v2.0.0-20211006190727-057b33161b90
 	github.com/stretchr/testify v1.8.4
@@ -25,7 +27,7 @@ require (
 	go.uber.org/atomic v1.11.0
 	go.uber.org/zap v1.24.0
 	google.golang.org/api v0.129.0
-	google.golang.org/grpc v1.56.1
+	google.golang.org/grpc v1.57.0
 	google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0
 	google.golang.org/protobuf v1.31.0
 	gopkg.in/yaml.v3 v3.0.1
@@ -34,8 +36,8 @@ require (
 require (
 	cloud.google.com/go/compute v1.20.1 // indirect
 	cloud.google.com/go/compute/metadata v0.2.3 // indirect
-	cloud.google.com/go/iam v0.13.0 // indirect
-	cloud.google.com/go/kms v1.10.1 // indirect
+	cloud.google.com/go/iam v1.1.1 // indirect
+	cloud.google.com/go/kms v1.12.1 // indirect
 	cloud.google.com/go/storage v1.30.1 // indirect
 	code.gitea.io/sdk/gitea v0.15.1 // indirect
 	github.com/AlekSi/pointer v1.2.0 // indirect
@@ -63,7 +65,6 @@ require (
 	github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c // indirect
 	github.com/acomagu/bufpipe v1.0.3 // indirect
 	github.com/alecthomas/jsonschema v0.0.0-20211209230136-e2b41affa5c1 // indirect
-	github.com/apex/log v1.9.0 // indirect
 	github.com/atc0005/go-teams-notify/v2 v2.6.1 // indirect
 	github.com/aws/aws-sdk-go v1.40.34 // indirect
 	github.com/aws/aws-sdk-go-v2 v1.9.0 // indirect
@@ -206,19 +207,19 @@ require (
 	go.opentelemetry.io/proto/otlp v0.20.0 // indirect
 	go.uber.org/multierr v1.9.0 // indirect
 	gocloud.dev v0.24.0 // indirect
-	golang.org/x/crypto v0.10.0 // indirect
-	golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
-	golang.org/x/net v0.11.0 // indirect
+	golang.org/x/crypto v0.11.0 // indirect
+	golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b // indirect
+	golang.org/x/net v0.13.0 // indirect
 	golang.org/x/oauth2 v0.9.0 // indirect
 	golang.org/x/sync v0.3.0 // indirect
-	golang.org/x/sys v0.9.0 // indirect
-	golang.org/x/text v0.10.0 // indirect
+	golang.org/x/sys v0.10.0 // indirect
+	golang.org/x/text v0.11.0 // indirect
 	golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
 	golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
-	google.golang.org/genproto v0.0.0-20230629202037-9506855d4529 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 // indirect
+	google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf // indirect
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 	gopkg.in/mail.v2 v2.3.1 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
diff --git a/go.sum b/go.sum
index 4c537887..09fd7ed5 100644
--- a/go.sum
+++ b/go.sum
@@ -31,8 +31,8 @@ cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+Y
 cloud.google.com/go v0.92.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
 cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
 cloud.google.com/go v0.94.0/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
-cloud.google.com/go v0.110.3 h1:wwearW+L7sAPSomPIgJ3bVn6Ck00HGQnn5HMLwf0azo=
-cloud.google.com/go v0.110.3/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
+cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk=
+cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
 cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
 cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
 cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@@ -47,11 +47,11 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7
 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
 cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
 cloud.google.com/go/firestore v1.5.0/go.mod h1:c4nNYR1qdq7eaZ+jSc5fonrQN2k3M7sWATcYTiakjEo=
-cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
-cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
+cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
+cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
 cloud.google.com/go/kms v0.1.0/go.mod h1:8Qp8PCAypHg4FdmlyW1QRAv09BGQ9Uzh7JnmIZxPk+c=
-cloud.google.com/go/kms v1.10.1 h1:7hm1bRqGCA1GBRQUrp831TwJ9TWhP+tvLuP497CQS2g=
-cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI=
+cloud.google.com/go/kms v1.12.1 h1:xZmZuwy2cwzsocmKDOPu4BL7umg8QXagQx6fKVmf45U=
+cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM=
 cloud.google.com/go/monitoring v0.1.0/go.mod h1:Hpm3XfzJv+UTiXzCG5Ffp0wijzHTC7Cv4eR7o3x/fEE=
 cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
 cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
@@ -319,6 +319,8 @@ github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrt
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
+github.com/glendc/go-external-ip v0.1.0 h1:iX3xQ2Q26atAmLTbd++nUce2P5ht5P4uD4V7caSY/xg=
+github.com/glendc/go-external-ip v0.1.0/go.mod h1:CNx312s2FLAJoWNdJWZ2Fpf5O4oLsMFwuYviHjS4uJE=
 github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
 github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 github.com/go-acme/lego v2.7.2+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M=
@@ -754,12 +756,12 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
-github.com/plgd-dev/device/v2 v2.2.1-0.20230602173355-efc3a81993c3 h1:whe0TFMRCL3rNkS2vuCj65oW/ukYjiMPhHvS3xqNUUc=
-github.com/plgd-dev/device/v2 v2.2.1-0.20230602173355-efc3a81993c3/go.mod h1:5PhpR7Gig3gn+hGZBIpyyAKtn7zaIwg5uQBMX3ST9dU=
+github.com/plgd-dev/device/v2 v2.2.1-0.20230803141537-60c77f6f4864 h1:56Qovnaj/dOjn9UK/m50/fUMidqUF/xiGU0YuqCr8eM=
+github.com/plgd-dev/device/v2 v2.2.1-0.20230803141537-60c77f6f4864/go.mod h1:lv93I6qbzHIDtXYdNBUkfTxpa7CdRia92OfVnjdU6ls=
 github.com/plgd-dev/go-coap/v2 v2.0.4-0.20200819112225-8eb712b901bc/go.mod h1:+tCi9Q78H/orWRtpVWyBgrr4vKFo2zYtbbxUllerBp4=
 github.com/plgd-dev/go-coap/v2 v2.4.1-0.20210517130748-95c37ac8e1fa/go.mod h1:rA7fc7ar+B/qa+Q0hRqv7yj/EMtIlmo1l7vkQGSrHPU=
-github.com/plgd-dev/go-coap/v3 v3.1.3 h1:zE2k8iFojeWdpZeROY16qk4nmvlE49AwXMzL+/Cw5ZE=
-github.com/plgd-dev/go-coap/v3 v3.1.3/go.mod h1:BnqQC1zU8tcjHAeCgqD5dpyrLKiSy/43q7JAO9Oj6xA=
+github.com/plgd-dev/go-coap/v3 v3.1.4-0.20230802114331-351cd00bab2d h1:dHabSkw9dQxuphK8s0lkm3EMlkyPo5C3ESQKbVrKT18=
+github.com/plgd-dev/go-coap/v3 v3.1.4-0.20230802114331-351cd00bab2d/go.mod h1:qJihIo3PI5NCzvMUE4eE0/T38PUw6Zg0JDq50Z8bQ2k=
 github.com/plgd-dev/hub/v2 v2.7.19 h1:W4Sq+IDTjvcMpQEu0nfe84t2bLCqk95SKj83tr80Szg=
 github.com/plgd-dev/hub/v2 v2.7.19/go.mod h1:2zKO5CAWWa2aKhbC1UAtG+urKeg/aoBTMfafdyqq/ww=
 github.com/plgd-dev/kit v0.0.0-20200819113605-d5fcf3e94f63/go.mod h1:Yl9zisyXfPdtP9hTWlJqjJYXmgU/jtSDKttz9/CeD90=
@@ -977,8 +979,8 @@ golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
 golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
-golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
-golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
+golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
+golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -989,8 +991,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
-golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
+golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI=
+golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1073,8 +1075,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
-golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
+golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY=
+golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1187,14 +1189,14 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
-golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
+golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
 golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
-golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
+golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1207,8 +1209,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
-golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
+golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1389,12 +1391,12 @@ google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEc
 google.golang.org/genproto v0.0.0-20210825212027-de86158e7fda/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
 google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20230629202037-9506855d4529 h1:9JucMWR7sPvCxUFd6UsOUNmA5kCcWOfORaT3tpAsKQs=
-google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
-google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529 h1:s5YSX+ZH5b5vS9rnpGymvIyMpLRJizowqDlOuyjXnTk=
-google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 h1:DEH99RbiLZhMxrpEJCZ0A+wdTe0EOgou/poSLx9vWf4=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
+google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e h1:xIXmWJ303kJCuogpj0bHq+dcjcZHU+XFyc1I0Yl9cRg=
+google.golang.org/genproto v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:0ggbjUrZYpy1q+ANUS30SEoGZ53cdfwtbuG7Ptgy108=
+google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 h1:XVeBY8d/FaK4848myy41HBqnDwvxeV3zMZhwN1TvAMU=
+google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf h1:guOdSPaeFgN+jEJwTo1dQ71hdBm+yKSCCKuTRkJzcVo=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20230731193218-e0aa005b6bdf/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1421,8 +1423,8 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
 google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
 google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
 google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
-google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ=
-google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
+google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
+google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y=
diff --git a/pkg/net/listener/tls/config.go b/pkg/net/listener/tls/config.go
new file mode 100644
index 00000000..f1e4136a
--- /dev/null
+++ b/pkg/net/listener/tls/config.go
@@ -0,0 +1,23 @@
+package tls
+
+import (
+	"fmt"
+	"net"
+
+	"github.com/plgd-dev/hub/v2/pkg/security/certManager/server"
+)
+
+type Config struct {
+	Addr string        `yaml:"address" json:"address"`
+	TLS  server.Config `yaml:"tls" json:"tls"`
+}
+
+func (c *Config) Validate() error {
+	if _, err := net.ResolveTCPAddr("tcp", c.Addr); err != nil {
+		return fmt.Errorf("address('%v') - %w", c.Addr, err)
+	}
+	if err := c.TLS.Validate(); err != nil {
+		return fmt.Errorf("tls.%w", err)
+	}
+	return nil
+}
diff --git a/pkg/net/listener/tls/listener.go b/pkg/net/listener/tls/listener.go
new file mode 100644
index 00000000..9f99968c
--- /dev/null
+++ b/pkg/net/listener/tls/listener.go
@@ -0,0 +1,155 @@
+package tls
+
+import (
+	"bufio"
+	"crypto/tls"
+	"fmt"
+	"net"
+	"time"
+
+	"github.com/plgd-dev/hub/v2/pkg/fsnotify"
+	"github.com/plgd-dev/hub/v2/pkg/log"
+	"github.com/plgd-dev/hub/v2/pkg/security/certManager/server"
+)
+
+type ConnectionType uint8
+
+const (
+	ConnectionTypeHTTP ConnectionType = iota
+	ConnectionTypeTLS
+)
+
+type Conn struct {
+	net.Conn
+	ConnectionType ConnectionType
+	buf            *bufio.Reader
+}
+
+func (c *Conn) Read(b []byte) (int, error) {
+	return c.buf.Read(b)
+}
+
+type chConn struct {
+	c   net.Conn
+	err error
+}
+
+type SplitListener struct {
+	net.Listener
+	config *tls.Config
+	cons   chan chConn
+	logger log.Logger
+}
+
+// tlsRecordHeaderLooksLikeHTTP reports whether a TLS record header
+// looks like it might've been a misdirected plaintext HTTP request.
+func tlsRecordHeaderLooksLikeHTTP(hdr []byte) bool {
+	if len(hdr) < 5 {
+		return false
+	}
+	switch string(hdr[:5]) {
+	case "GET /", "HEAD ", "POST ", "PUT /", "OPTIO":
+		return true
+	}
+	return false
+}
+
+// prepareConnection accepts connections asynchronously to avoid blocking subsequent connection attempts due to potential blocking caused by c.Peek.
+// The use of asynchronous connection acceptance ensures that the program can continue accepting incoming connections while waiting for c.Peek to complete
+func (l *SplitListener) prepareConnection(c net.Conn) {
+	// buffer reads on our conn
+	bconn := &Conn{
+		Conn: c,
+		buf:  bufio.NewReader(c),
+	}
+	n := 5
+	_ = c.SetReadDeadline(time.Now().Add(1 * time.Second))
+	hdr, err := bconn.buf.Peek(n)
+	_ = c.SetReadDeadline(time.Time{})
+	if err != nil {
+		l.logger.Warnf("closing connection for error while peeking for first 5bytes in 1sec: %v", err)
+		_ = bconn.Close()
+		return
+	}
+	// The first 5 bytes is a TLS header, so we can use it to check for HTTP
+	if tlsRecordHeaderLooksLikeHTTP(hdr) {
+		bconn.ConnectionType = ConnectionTypeHTTP
+		l.cons <- chConn{c: bconn, err: nil}
+		return
+	}
+	bconn.ConnectionType = ConnectionTypeTLS
+	l.cons <- chConn{c: tls.Server(bconn, l.config), err: nil}
+}
+
+func (l *SplitListener) run() {
+	for {
+		c, err := l.Listener.Accept()
+		if err != nil {
+			l.cons <- chConn{nil, err}
+			return
+		}
+		go l.prepareConnection(c)
+	}
+}
+
+func (l *SplitListener) Accept() (net.Conn, error) {
+	d := <-l.cons
+	return d.c, d.err
+}
+
+func NewSplitListener(l net.Listener, config *tls.Config, logger log.Logger) net.Listener {
+	sl := &SplitListener{
+		Listener: l,
+		config:   config,
+		cons:     make(chan chConn),
+		logger:   logger,
+	}
+	go sl.run()
+	return sl
+}
+
+// Server handles gRPC requests to the service.
+type Server struct {
+	listener  net.Listener
+	closeFunc []func()
+}
+
+// NewServer instantiates a listen server.
+// When passing addr with an unspecified port or ":", use Addr().
+func New(config Config, fileWatcher *fsnotify.Watcher, logger log.Logger) (*Server, error) {
+	certManager, err := server.New(config.TLS, fileWatcher, logger)
+	if err != nil {
+		return nil, fmt.Errorf("cannot create cert manager %w", err)
+	}
+
+	lis, err := net.Listen("tcp", config.Addr)
+	if err != nil {
+		certManager.Close()
+		return nil, fmt.Errorf("listening failed: %w", err)
+	}
+	splitListener := NewSplitListener(lis, certManager.GetTLSConfig(), logger)
+
+	return &Server{listener: splitListener, closeFunc: []func(){certManager.Close}}, nil
+}
+
+// AddCloseFunc adds a function to be called by the Close method.
+// This eliminates the need for wrapping the Server.
+func (s *Server) AddCloseFunc(f func()) {
+	s.closeFunc = append(s.closeFunc, f)
+}
+
+func (s *Server) Close() error {
+	err := s.listener.Close()
+	for _, f := range s.closeFunc {
+		f()
+	}
+	return err
+}
+
+func (s *Server) Accept() (net.Conn, error) {
+	return s.listener.Accept()
+}
+
+func (s *Server) Addr() net.Addr {
+	return s.listener.Addr()
+}
diff --git a/service/config/config.go b/service/config/config.go
index 1646c0a3..75fbc290 100644
--- a/service/config/config.go
+++ b/service/config/config.go
@@ -120,7 +120,7 @@ func (c Config) Store() error {
 	return Store(c, c.configPath)
 }
 
-func DefaultConfig(uiDirectory string) Config {
+func DefaultConfig(directory string) Config {
 	logCfg := log.MakeDefaultConfig()
 	logCfg.Encoding = "console"
 	return Config{
@@ -128,11 +128,11 @@ func DefaultConfig(uiDirectory string) Config {
 		APIs: APIsConfig{
 			HTTP: HTTPConfig{
 				Enabled: true,
-				Config:  http.DefaultConfig(uiDirectory),
+				Config:  http.DefaultConfig(directory),
 			},
 			GRPC: GRPCConfig{
 				Enabled: true,
-				Config:  grpc.DefaultConfig(),
+				Config:  grpc.DefaultConfig(directory),
 			},
 		},
 		Clients: ClientsConfig{
diff --git a/service/config/grpc/config.go b/service/config/grpc/config.go
index f22bf37c..525fcd6c 100644
--- a/service/config/grpc/config.go
+++ b/service/config/grpc/config.go
@@ -17,27 +17,34 @@
 package grpc
 
 import (
+	"path"
 	"time"
 
 	"github.com/plgd-dev/client-application/pb"
 	"github.com/plgd-dev/client-application/pkg/net/grpc/server"
 	grpcServer "github.com/plgd-dev/hub/v2/pkg/net/grpc/server"
+	certManagerServer "github.com/plgd-dev/hub/v2/pkg/security/certManager/server"
 )
 
 type Config = server.Config
 
 type ServiceInformation = pb.BuildInfo
 
-var defaultConfig = Config{
-	Addr: ":8081",
-	TLS: server.TLSConfig{
-		Enabled: false,
-	},
-	EnforcementPolicy: grpcServer.EnforcementPolicyConfig{
-		MinTime: 5 * time.Minute,
-	},
-}
-
-func DefaultConfig() Config {
-	return defaultConfig
+func DefaultConfig(directory string) Config {
+	return Config{
+		Addr: ":8081",
+		TLS: server.TLSConfig{
+			Enabled: true,
+			Config: certManagerServer.Config{
+				// we use the same cert for CA because certManagerServer.Config doesn't allow nil values
+				CAPool:                    path.Join(directory, "certs", "crt.pem"),
+				KeyFile:                   path.Join(directory, "certs", "key.pem"),
+				CertFile:                  path.Join(directory, "certs", "crt.pem"),
+				ClientCertificateRequired: false,
+			},
+		},
+		EnforcementPolicy: grpcServer.EnforcementPolicyConfig{
+			MinTime: 5 * time.Minute,
+		},
+	}
 }
diff --git a/service/config/http/config.go b/service/config/http/config.go
index fe4834a8..fca229a6 100644
--- a/service/config/http/config.go
+++ b/service/config/http/config.go
@@ -18,10 +18,12 @@ package http
 
 import (
 	"fmt"
+	"path"
 	"time"
 
 	"github.com/plgd-dev/client-application/pkg/net/listener"
 	"github.com/plgd-dev/hub/v2/pkg/net/http/server"
+	certManagerServer "github.com/plgd-dev/hub/v2/pkg/security/certManager/server"
 )
 
 type CORSConfig struct {
@@ -57,12 +59,19 @@ func (c *Config) Validate() error {
 	return c.Config.Validate()
 }
 
-func DefaultConfig(uiDirectory string) Config {
+func DefaultConfig(directory string) Config {
 	return Config{
 		Config: listener.Config{
 			Addr: ":8080",
 			TLS: listener.TLSConfig{
-				Enabled: false,
+				Enabled: true,
+				Config: certManagerServer.Config{
+					// we use the same cert for CA because certManagerServer.Config doesn't allow nil values
+					CAPool:                    path.Join(directory, "certs", "crt.pem"),
+					KeyFile:                   path.Join(directory, "certs", "key.pem"),
+					CertFile:                  path.Join(directory, "certs", "crt.pem"),
+					ClientCertificateRequired: false,
+				},
 			},
 		},
 		CORS: CORSConfig{
@@ -72,7 +81,7 @@ func DefaultConfig(uiDirectory string) Config {
 		},
 		UI: UIConfig{
 			Enabled:   true,
-			Directory: uiDirectory,
+			Directory: path.Join(directory, "www"),
 		},
 		Server: server.Config{
 			ReadTimeout:       time.Second * 8,
diff --git a/service/http/service.go b/service/http/service.go
index 9d3f0f45..2e109309 100644
--- a/service/http/service.go
+++ b/service/http/service.go
@@ -20,6 +20,7 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"net"
 	"net/http"
 	"net/http/httptest"
 	"regexp"
@@ -31,6 +32,7 @@ import (
 	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
 	"github.com/plgd-dev/client-application/pb"
 	"github.com/plgd-dev/client-application/pkg/net/listener"
+	"github.com/plgd-dev/client-application/pkg/net/listener/tls"
 	configHttp "github.com/plgd-dev/client-application/service/config/http"
 	"github.com/plgd-dev/client-application/service/grpc"
 	"github.com/plgd-dev/hub/v2/http-gateway/serverMux"
@@ -105,21 +107,48 @@ func createAuthFunc(config configHttp.Config, clientApplicationServer *grpc.Clie
 	return auth
 }
 
-// New creates new HTTP service
-func New(ctx context.Context, serviceName string, config configHttp.Config, clientApplicationServer *grpc.ClientApplicationServer, fileWatcher *fsnotify.Watcher, logger log.Logger, tracerProvider trace.TracerProvider) (*Service, error) {
-	listener, err := listener.New(config.Config, fileWatcher, logger)
-	if err != nil {
-		return nil, fmt.Errorf("cannot create grpc server: %w", err)
-	}
+type contextKey struct {
+	key string
+}
 
-	ch := new(inprocgrpc.Channel)
-	pb.RegisterClientApplicationServer(ch, clientApplicationServer)
-	grpcClient := pb.NewClientApplicationClient(ch)
+var connContextKey = &contextKey{"http-conn"}
 
-	auth := createAuthFunc(config, clientApplicationServer)
-	mux := serverMux.New()
-	r := serverMux.NewRouter(queryCaseInsensitive, auth)
+func saveConnInContext(ctx context.Context, c net.Conn) context.Context {
+	return context.WithValue(ctx, connContextKey, c)
+}
+
+func getTLSConn(r *http.Request) (*tls.Conn, bool) {
+	c, ok := r.Context().Value(connContextKey).(*tls.Conn)
+	return c, ok
+}
 
+func newListener(config configHttp.Config, fileWatcher *fsnotify.Watcher, logger log.Logger) (listener.Listener, error) {
+	if config.Config.TLS.Enabled {
+		return tls.New(tls.Config{
+			Addr: config.Config.Addr,
+			TLS:  config.Config.TLS.Config,
+		}, fileWatcher, logger)
+	} else {
+		return listener.New(config.Config, fileWatcher, logger)
+	}
+}
+
+func wrapHandler(handler http.Handler, serviceName string, tracerProvider trace.TracerProvider) http.Handler {
+	h := kitNetHttp.OpenTelemetryNewHandler(handler, serviceName, tracerProvider)
+	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		if c, ok := getTLSConn(r); ok && c.ConnectionType == tls.ConnectionTypeHTTP {
+			http.Redirect(w, r, "https://"+r.Host+r.RequestURI, http.StatusMovedPermanently)
+			if f, ok := w.(http.Flusher); ok {
+				f.Flush()
+			}
+			_ = c.Close()
+			return
+		}
+		h.ServeHTTP(w, r)
+	})
+}
+
+func newCORSHandler(config configHttp.Config, r *router.Router) http.Handler {
 	corsOptions := make([]handlers.CORSOption, 0, 5)
 	corsOptions = append(corsOptions, handlers.AllowedHeaders(config.CORS.AllowedHeaders))
 	corsOptions = append(corsOptions, handlers.AllowedOrigins(config.CORS.AllowedOrigins))
@@ -127,18 +156,10 @@ func New(ctx context.Context, serviceName string, config configHttp.Config, clie
 	if config.CORS.AllowCredentials {
 		corsOptions = append(corsOptions, handlers.AllowCredentials())
 	}
-	handler := handlers.CORS(corsOptions...)(r)
+	return handlers.CORS(corsOptions...)(r)
+}
 
-	// register grpc-proxy handler
-	if err := pb.RegisterClientApplicationHandlerClient(context.Background(), mux, grpcClient); err != nil {
-		_ = listener.Close()
-		return nil, fmt.Errorf("failed to register grpc-gateway handler: %w", err)
-	}
-	requestHandler := &RequestHandler{mux: mux, clientApplicationServer: clientApplicationServer, config: config}
-	r.PathPrefix(Devices).Methods(http.MethodPut).MatcherFunc(resourceMatcher).HandlerFunc(requestHandler.updateResource)
-	r.PathPrefix(Devices).Methods(http.MethodPost).MatcherFunc(resourceMatcher).HandlerFunc(requestHandler.createResource)
-	r.PathPrefix(ApiV1).Handler(mux)
-	r.PathPrefix(WellKnown).Handler(mux)
+func setUIHandlers(config configHttp.Config, r *router.Router) {
 	// serve www directory
 	if config.UI.Enabled {
 		fs := http.FileServer(http.Dir(config.UI.Directory))
@@ -159,18 +180,49 @@ func New(ctx context.Context, serviceName string, config configHttp.Config, clie
 			}
 		}))
 	}
+}
+
+// New creates new HTTP service
+func New(ctx context.Context, serviceName string, config configHttp.Config, clientApplicationServer *grpc.ClientApplicationServer, fileWatcher *fsnotify.Watcher, logger log.Logger, tracerProvider trace.TracerProvider) (*Service, error) {
+	lis, err := newListener(config, fileWatcher, logger)
+	if err != nil {
+		return nil, fmt.Errorf("cannot create grpc server: %w", err)
+	}
+
+	ch := new(inprocgrpc.Channel)
+	pb.RegisterClientApplicationServer(ch, clientApplicationServer)
+	grpcClient := pb.NewClientApplicationClient(ch)
+
+	auth := createAuthFunc(config, clientApplicationServer)
+	mux := serverMux.New()
+	r := serverMux.NewRouter(queryCaseInsensitive, auth)
+	handler := newCORSHandler(config, r)
+
+	// register grpc-proxy handler
+	if err := pb.RegisterClientApplicationHandlerClient(context.Background(), mux, grpcClient); err != nil {
+		_ = lis.Close()
+		return nil, fmt.Errorf("failed to register grpc-gateway handler: %w", err)
+	}
+	requestHandler := &RequestHandler{mux: mux, clientApplicationServer: clientApplicationServer, config: config}
+	r.PathPrefix(Devices).Methods(http.MethodPut).MatcherFunc(resourceMatcher).HandlerFunc(requestHandler.updateResource)
+	r.PathPrefix(Devices).Methods(http.MethodPost).MatcherFunc(resourceMatcher).HandlerFunc(requestHandler.createResource)
+	r.PathPrefix(ApiV1).Handler(mux)
+	r.PathPrefix(WellKnown).Handler(mux)
+
+	setUIHandlers(config, r)
 
 	httpServer := &http.Server{
-		Handler:           kitNetHttp.OpenTelemetryNewHandler(handler, serviceName, tracerProvider),
+		Handler:           wrapHandler(handler, serviceName, tracerProvider),
 		ReadTimeout:       config.Server.ReadTimeout,
 		ReadHeaderTimeout: config.Server.ReadHeaderTimeout,
 		WriteTimeout:      config.Server.WriteTimeout,
 		IdleTimeout:       config.Server.IdleTimeout,
+		ConnContext:       saveConnInContext,
 	}
 
 	return &Service{
 		httpServer: httpServer,
-		listener:   listener,
+		listener:   lis,
 	}, nil
 }
 
