diff --git a/errors/error.go b/errors/error.go
index 31b442d016f3d408f946688ef0895b5addeee3e3..b4baf2fd88e6da367aaf783f53ffd42da1c85b66 100644
--- a/errors/error.go
+++ b/errors/error.go
@@ -177,6 +177,14 @@ func pkgStack(err error, opts FormattingOptions) string {
 				s += fmt.Sprintf("%s", e)
 			}
 			return s
+		} else {
+			//no stack tracer...
+			if opts.NewLines {
+				s += ", because \n\t"
+			} else {
+				s += ", because "
+			}
+			s += e.Error()
 		}
 		errWithCause, ok := e.(ErrorWithCause)
 		if !ok {
diff --git a/go.mod b/go.mod
index 38c85f6088f9244cb90b22f9e8be46008ab7f44a..e90b1e04ffa5949a3f40256c3df0f0053af4e0d0 100644
--- a/go.mod
+++ b/go.mod
@@ -27,7 +27,10 @@ require (
 
 require (
 	github.com/aws/aws-sdk-go v1.40.50 // indirect
+	github.com/cespare/xxhash/v2 v2.1.1 // indirect
+	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 	github.com/fatih/color v1.13.0 // indirect
+	github.com/go-redis/redis/v8 v8.11.3 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
 	github.com/mattn/go-colorable v0.1.9 // indirect
 	github.com/mattn/go-isatty v0.0.14 // indirect
diff --git a/go.sum b/go.sum
index 70dc620d48f589101277bab909c1f17b6c599823..732bbf4426935a37d6b94894adb28668d189f9e1 100644
--- a/go.sum
+++ b/go.sum
@@ -5,12 +5,16 @@ github.com/aws/aws-lambda-go v1.26.0/go.mod h1:jJmlefzPfGnckuHdXX7/80O3BvUUi12XO
 github.com/aws/aws-sdk-go v1.40.50 h1:QP4NC9EZWBszbNo2UbG6bbObMtN35kCFb4h0r08q884=
 github.com/aws/aws-sdk-go v1.40.50/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
@@ -22,6 +26,9 @@ github.com/go-pg/pg/v10 v10.10.5 h1:RRW8NqxVu4vgzN9k05TT9rM5X+2VQHcIBRLeK9djMBE=
 github.com/go-pg/pg/v10 v10.10.5/go.mod h1:EmoJGYErc+stNN/1Jf+o4csXuprjxcRztBnn6cHe38E=
 github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU=
 github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo=
+github.com/go-redis/redis/v8 v8.11.3 h1:GCjoYp8c+yQTJfc0n69iwSiHjvuAdruxl7elnZCxgt8=
+github.com/go-redis/redis/v8 v8.11.3/go.mod h1:xNJ9xDG09FsIPwh3bWdk+0oDWHbtF9rPN0F/oD9XeKc=
+github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -34,12 +41,15 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
 github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -61,14 +71,18 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
 github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
+github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
 github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
+github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU=
+github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -99,8 +113,10 @@ github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vb
 github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
 github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
 github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
 golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
@@ -108,21 +124,26 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
 golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -136,6 +157,7 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7 h1:iGu644GcxtEcrInvDsQRCwJjtCIOlT2V7IRt6ah2Whw=
 golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -154,8 +176,13 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -173,6 +200,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
@@ -185,6 +214,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/redis/redis.go b/redis/redis.go
new file mode 100644
index 0000000000000000000000000000000000000000..23d667e5a1b39bed142a5409419b364f5617f1f9
--- /dev/null
+++ b/redis/redis.go
@@ -0,0 +1,165 @@
+package redis
+
+import (
+	"context"
+	"encoding/json"
+	"os"
+	"reflect"
+	"time"
+
+	"github.com/go-redis/redis/v8"
+	"gitlab.com/uafrica/go-utils/errors"
+	"gitlab.com/uafrica/go-utils/logger"
+)
+
+type IRedis interface {
+	Del(key string) error
+	SetJSON(key string, value interface{}) error
+	SetJSONIndefinitely(key string, value interface{}) error
+	SetJSONForDur(key string, value interface{}, dur time.Duration) error
+	GetJSON(key string, valueType reflect.Type) (value interface{}, ok bool)
+	SetString(key string, value string) error
+	SetStringIndefinitely(key string, value string) error
+	SetStringForDur(key string, value string, dur time.Duration) error
+	GetString(key string) (value string, ok bool)
+}
+
+type redisWithContext struct {
+	context.Context
+	client *redis.Client
+}
+
+func New(ctx context.Context) (IRedis, error) {
+	if globalClient == nil {
+		var err error
+		if globalClient, err = connect(); err != nil {
+			return redisWithContext{Context: ctx}, errors.Wrapf(err, "cannot connect to REDIS")
+		}
+	}
+	return redisWithContext{
+		Context: ctx,
+		client:  globalClient,
+	}, nil
+}
+
+func (r redisWithContext) Del(key string) error {
+	if r.client == nil {
+		return errors.Errorf("REDIS disabled: cannot del key(%s)", key)
+	}
+	_, err := r.client.Del(r.Context, key).Result()
+	if err != nil {
+		return errors.Wrapf(err, "failed to del key(%s)", key)
+	}
+	logger.Debugf("REDIS.Del(%s)", key)
+	return nil
+}
+
+//set JSON value for 24h
+func (r redisWithContext) SetJSON(key string, value interface{}) error {
+	return r.SetJSONForDur(key, value, 24*time.Hour)
+}
+
+func (r redisWithContext) SetJSONIndefinitely(key string, value interface{}) error {
+	return r.SetJSONForDur(key, value, 0)
+}
+
+func (r redisWithContext) SetJSONForDur(key string, value interface{}, dur time.Duration) error {
+	if r.client == nil {
+		return errors.Errorf("REDIS disabled: cannot set JSON key(%s) = (%T)%v", key, value, value)
+	}
+	jsonBytes, err := json.Marshal(value)
+	if err != nil {
+		return errors.Wrapf(err, "failed to JSON encode key(%s) = (%T)", key, value)
+	}
+	if _, err = r.client.Set(r.Context, key, string(jsonBytes), dur).Result(); err != nil {
+		return errors.Wrapf(err, "failed to set JSON key(%s)", key)
+	}
+	logger.Debugf("REDIS.SetJSON(%s)=%s (%T) (exp: %v)", key, string(jsonBytes), value, dur)
+	return nil
+}
+
+//return:
+//	nil,nil if key is not defined
+//	nil,err if failed to get/determine if it exists, or failed to decode
+//	<value>,nil if found and decoded
+func (r redisWithContext) GetJSON(key string, valueType reflect.Type) (value interface{}, ok bool) {
+	if r.client == nil {
+		return nil, false
+	}
+	jsonValue, err := r.client.Get(r.Context, key).Result()
+	if err != nil {
+		return nil, false
+	}
+	newValuePtr := reflect.New(valueType)
+	if err := json.Unmarshal([]byte(jsonValue), newValuePtr.Interface()); err != nil {
+		return nil, false
+	}
+	return newValuePtr.Elem().Interface(), true
+}
+
+func (r redisWithContext) SetString(key string, value string) error {
+	return r.SetStringForDur(key, value, 24*time.Hour)
+}
+
+func (r redisWithContext) SetStringIndefinitely(key string, value string) error {
+	return r.SetStringForDur(key, value, 0)
+}
+
+func (r redisWithContext) SetStringForDur(key string, value string, dur time.Duration) error {
+	if r.client == nil {
+		return errors.Errorf("REDIS disabled: cannot set key(%s) = (%T)%v", key, value, value)
+	}
+	if _, err := r.client.Set(r.Context, key, value, dur).Result(); err != nil {
+		return errors.Wrapf(err, "failed to set key(%s)", key)
+	}
+	logger.Debugf("REDIS.SetString(%s)=%s (exp: %v)", key, value, dur)
+	return nil
+}
+
+func (r redisWithContext) GetString(key string) (string, bool) {
+	if r.client == nil {
+		return "", false
+	}
+	value, err := r.client.Get(r.Context, key).Result()
+	if err != nil { /* Actual error */
+		if err != redis.Nil { /* other than Key does not exist */
+			logger.Errorf("Error fetching redis key(%s): %+v", key, err)
+		}
+		return "", false
+	}
+	return value, true
+}
+
+//global connection to REDIS used in all context
+var globalClient *redis.Client
+
+func connect() (*redis.Client, error) {
+	host := os.Getenv("REDIS_HOST")
+	if host == "false" {
+		return nil, errors.Errorf("REDIS_HOST=false")
+	}
+
+	port := os.Getenv("REDIS_PORT")
+	if os.Getenv("DEBUGGING") == "true" {
+		host = "host.docker.internal"
+		if os.Getenv("LOCAL") == "true" {
+			host = "localhost"
+		}
+		env := os.Getenv("ENVIRONMENT")
+		switch env {
+		case "dev":
+			port = "6380"
+		case "stage":
+			port = "6381"
+		case "prod":
+			port = "6383"
+		}
+	}
+	logger.Debugf("Using REDIS(%s:%s)", host, port)
+	globalClient = redis.NewClient(&redis.Options{
+		Addr:     host + ":" + port,
+		Password: "", // no password set
+		DB:       0,  // use default DB
+	})
+	return globalClient, nil
+} //connect()
diff --git a/redis/redis_test.go b/redis/redis_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f6558892f934a0b37b169709662089301cac2e58
--- /dev/null
+++ b/redis/redis_test.go
@@ -0,0 +1,145 @@
+package redis_test
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"reflect"
+	"testing"
+	"time"
+
+	"gitlab.com/uafrica/go-utils/redis"
+)
+
+func TestString(t *testing.T) {
+	os.Setenv("REDIS_HOST", "localhost")
+	os.Setenv("REDIS_PORT", "6380")
+	ctx := context.Background()
+	r, err := redis.New(ctx)
+	if err != nil {
+		t.Fatalf("failed to create redis: %+v", err)
+	}
+	id := fmt.Sprintf("%s_%v", t.Name(), time.Now().Unix())
+	defer func() {
+		r.Del(id)
+	}()
+
+	value := "abc123"
+	if err := r.SetString(id, value); err != nil {
+		t.Fatalf("failed to set: (%T) %+v", err, err)
+	}
+
+	//get after set must return same value
+	if v, ok := r.GetString(id); !ok {
+		t.Fatalf("failed to get(%s)", id)
+	} else {
+		if v != value {
+			t.Fatalf("%s=%s != %s", id, v, value)
+		}
+	}
+
+	//must be able to delete
+	if err := r.Del(id); err != nil {
+		t.Fatalf("failed to del(%s): %+v", id, err)
+	}
+
+	//delete non-existing must also succeed
+	if err := r.Del(id); err != nil {
+		t.Fatalf("failed to del(%s) again: %+v", id, err)
+	}
+
+	//get after delete must indicate not exist
+	if _, ok := r.GetString(id); ok {
+		t.Fatalf("got(%s) after delete", id)
+	}
+}
+
+func TestJSON(t *testing.T) {
+	os.Setenv("REDIS_HOST", "localhost")
+	os.Setenv("REDIS_PORT", "6380")
+	ctx := context.Background()
+	r, err := redis.New(ctx)
+	if err != nil {
+		t.Fatalf("failed to create redis: %+v", err)
+	}
+	id := fmt.Sprintf("%s_%v", t.Name(), time.Now().Unix())
+	defer func() {
+		r.Del(id)
+	}()
+
+	type Person struct {
+		Name    string    `json:"name"`
+		Surname string    `json:"surname"`
+		Count   int       `json:"count"`
+		Dob     time.Time `json:"dob"`
+	}
+	dob, err := time.Parse("2006-01-02", "1986-06-28")
+	if err != nil {
+		t.Fatalf("invalid dob: %+v", err)
+	}
+	value := Person{"Joe", "Blogs", 25, dob}
+	if err := r.SetJSON(id, value); err != nil {
+		t.Fatalf("failed to set: (%T) %+v", err, err)
+	}
+
+	//get after set must return same value
+	if v, ok := r.GetJSON(id, reflect.TypeOf(Person{})); !ok {
+		t.Fatalf("failed to get(%s): %+v", id, err)
+	} else {
+		if v != value {
+			t.Fatalf("%s=%+v != %+v", id, v, value)
+		}
+	}
+
+	//must be able to delete
+	if err := r.Del(id); err != nil {
+		t.Fatalf("failed to del(%s): %+v", id, err)
+	}
+
+	//delete non-existing must also succeed
+	if err := r.Del(id); err != nil {
+		t.Fatalf("failed to del(%s) again: %+v", id, err)
+	}
+
+	//get after delete must indicate not exist
+	if v, ok := r.GetJSON(id, reflect.TypeOf(Person{})); ok {
+		t.Fatalf("got(%s) after delete: %+v", id, v)
+	}
+}
+
+func TestExp(t *testing.T) {
+	os.Setenv("REDIS_HOST", "localhost")
+	os.Setenv("REDIS_PORT", "6380")
+	ctx := context.Background()
+	r, err := redis.New(ctx)
+	if err != nil {
+		t.Fatalf("failed to create redis: %+v", err)
+	}
+	id := fmt.Sprintf("%s_%v", t.Name(), time.Now().Unix())
+	defer func() {
+		r.Del(id)
+	}()
+
+	value := "abc123"
+	if err := r.SetStringForDur(id, value, time.Second); err != nil {
+		t.Fatalf("failed to set: (%T) %+v", err, err)
+	}
+
+	//get after set must return same value
+	if v, ok := r.GetString(id); !ok {
+		t.Fatalf("failed to get(%s)", id)
+	} else {
+		if v != value {
+			t.Fatalf("%s=%s != %s", id, v, value)
+		}
+	}
+
+	//wait 5 seconds
+	t.Logf("waiting 5seconds for key to expire...")
+	time.Sleep(time.Second * 5)
+
+	//get after delete expire must fail
+	if _, ok := r.GetString(id); ok {
+		t.Fatalf("got(%s) after expiry", id)
+	}
+}