diff --git a/go.mod b/go.mod
index c4bc532e1b85714da9e5a283f5b60f65748fa1f5..253f5e966f691df3ae7bdb3a2ae67930f4e452ed 100644
--- a/go.mod
+++ b/go.mod
@@ -19,36 +19,30 @@ require (
 	go.uber.org/zap v1.19.1
 	golang.org/x/crypto v0.8.0
 	golang.org/x/net v0.9.0
+	golang.org/x/oauth2 v0.4.0
 	google.golang.org/grpc v1.54.0
 	google.golang.org/protobuf v1.28.1
 	gopkg.in/yaml.v3 v3.0.1
 )
 
 require (
-	github.com/Masterminds/goutils v1.1.1 // indirect
-	github.com/Masterminds/semver/v3 v3.2.1 // indirect
-	github.com/Masterminds/sprig/v3 v3.2.3 // indirect
+	cloud.google.com/go/compute v1.15.1 // indirect
+	cloud.google.com/go/compute/metadata v0.2.3 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/go-kit/log v0.2.0 // indirect
 	github.com/go-logfmt/logfmt v0.5.1 // indirect
 	github.com/golang/snappy v0.0.1 // indirect
-	github.com/google/uuid v1.3.0 // indirect
 	github.com/gosimple/unidecode v1.0.1 // indirect
 	github.com/hashicorp/errwrap v1.0.0 // indirect
-	github.com/hexdigest/gowrap v1.3.2 // indirect
-	github.com/huandu/xstrings v1.4.0 // indirect
-	github.com/imdario/mergo v0.3.15 // indirect
 	github.com/klauspost/compress v1.13.6 // indirect
-	github.com/mitchellh/copystructure v1.2.0 // indirect
-	github.com/mitchellh/reflectwalk v1.0.2 // indirect
+	github.com/kr/pretty v0.3.0 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
 	github.com/nats-io/nkeys v0.3.0 // indirect
 	github.com/nats-io/nuid v1.0.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/shopspring/decimal v1.3.1 // indirect
-	github.com/spf13/cast v1.5.0 // indirect
+	github.com/rogpeppe/go-internal v1.8.1 // indirect
 	github.com/stretchr/objx v0.4.0 // indirect
 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
 	github.com/xdg-go/scram v1.1.1 // indirect
@@ -56,10 +50,10 @@ require (
 	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
 	go.uber.org/atomic v1.9.0 // indirect
 	go.uber.org/multierr v1.7.0 // indirect
-	golang.org/x/mod v0.10.0 // indirect
 	golang.org/x/sync v0.1.0 // indirect
 	golang.org/x/sys v0.7.0 // indirect
 	golang.org/x/text v0.9.0 // indirect
-	golang.org/x/tools v0.8.0 // indirect
+	google.golang.org/appengine v1.6.7 // indirect
 	google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
+	gopkg.in/yaml.v2 v2.3.0 // indirect
 )
diff --git a/go.sum b/go.sum
index 53493f56d5ddbe17ec5350541663552f0dcb311c..6a9f211c41832d921925c919cbf2bfb8e2bcab5f 100644
--- a/go.sum
+++ b/go.sum
@@ -1,15 +1,13 @@
+cloud.google.com/go/compute v1.15.1 h1:7UGq3QknM33pw5xATlpzeoomNxsacIVvTqTTvbfajmE=
+cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA=
+cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
+cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
 github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
-github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
-github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
-github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
-github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
-github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
-github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
-github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
 github.com/antonmedv/expr v1.9.0 h1:j4HI3NHEdgDnN9p6oI6Ndr0G5QryMY0FNxT4ONrFDGU=
 github.com/antonmedv/expr v1.9.0/go.mod h1:5qsM3oLGDND7sDmQGDXHkYfkjYMUX14qsgqmHhwGEk8=
 github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 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=
@@ -24,6 +22,7 @@ github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw=
 github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
 github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
 github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
@@ -33,9 +32,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-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/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q=
 github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
 github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
@@ -46,41 +42,27 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
 github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
 github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hexdigest/gowrap v1.3.2 h1:ZDhDFhrbAHYRdt9ZnULKZyggC/3+W9EpfX6R8DjlggY=
-github.com/hexdigest/gowrap v1.3.2/go.mod h1:g8N2jI4n9AKrf843erksNTrt4sdkG+TGVfhWe8dWrJQ=
-github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
-github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
-github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
-github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
-github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
 github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
 github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
-github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
-github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
-github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
 github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo=
 github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
-github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
-github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -96,6 +78,7 @@ github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
 github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
 github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
 github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -104,20 +87,16 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rivo/tview v0.0.0-20200219210816-cd38d7432498/go.mod h1:6lkG1x+13OShEf0EaOCaTQYyB7d5nSbb181KtjlS+84=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
+github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
 github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
 github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
 github.com/sanity-io/litter v1.2.0/go.mod h1:JF6pZUFgu2Q0sBZ+HSV35P8TVPI1TTzEwyu9FXAw2W4=
-github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
-github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
-github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
-github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
-github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -136,7 +115,6 @@ github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgk
 github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
 github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas=
 go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -152,34 +130,24 @@ go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
 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-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
-golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
 golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
 golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
 golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
-golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
-golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
 golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
 golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
+golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M=
+golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -191,24 +159,14 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
 golang.org/x/sys v0.7.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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
@@ -216,13 +174,12 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
-golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
 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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
+google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
 google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
 google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag=
@@ -234,9 +191,11 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
 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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/pkg/content/client/client.go b/pkg/content/client/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..500e213792edf4d515a40810929ee77183c9e471
--- /dev/null
+++ b/pkg/content/client/client.go
@@ -0,0 +1,208 @@
+package client
+
+import (
+	"context"
+	"crypto/tls"
+	"crypto/x509"
+	"errors"
+	"fmt"
+	"net/url"
+	"time"
+
+	"git.perx.ru/perxis/perxis-go/pkg/cache"
+	clientsSvc "git.perx.ru/perxis/perxis-go/pkg/clients/middleware"
+	clientsTransportGrpc "git.perx.ru/perxis/perxis-go/pkg/clients/transport/grpc"
+	collaboratorsSvc "git.perx.ru/perxis/perxis-go/pkg/collaborators/middleware"
+	collaboratorsTransportGrpc "git.perx.ru/perxis/perxis-go/pkg/collaborators/transport/grpc"
+	collectionsSvc "git.perx.ru/perxis/perxis-go/pkg/collections/middleware"
+	collectionsTransportGrpc "git.perx.ru/perxis/perxis-go/pkg/collections/transport/grpc"
+	"git.perx.ru/perxis/perxis-go/pkg/content"
+	environmentsSvc "git.perx.ru/perxis/perxis-go/pkg/environments/middleware"
+	environmentsTransportGrpc "git.perx.ru/perxis/perxis-go/pkg/environments/transport/grpc"
+	invitationsSvc "git.perx.ru/perxis/perxis-go/pkg/invitations/middleware"
+	invitationsTransportGrpc "git.perx.ru/perxis/perxis-go/pkg/invitations/transport/grpc"
+	itemsSvc "git.perx.ru/perxis/perxis-go/pkg/items/middleware"
+	itemsTransportGrpc "git.perx.ru/perxis/perxis-go/pkg/items/transport/grpc"
+	localesSvc "git.perx.ru/perxis/perxis-go/pkg/locales/middleware"
+	localsTransportGrpc "git.perx.ru/perxis/perxis-go/pkg/locales/transport/grpc"
+	referencesSvc "git.perx.ru/perxis/perxis-go/pkg/references/middleware"
+	referencesTransportGrpc "git.perx.ru/perxis/perxis-go/pkg/references/transport/grpc"
+	rolesSvc "git.perx.ru/perxis/perxis-go/pkg/roles/middleware"
+	rolesTransportGrpc "git.perx.ru/perxis/perxis-go/pkg/roles/transport/grpc"
+	spacesSvc "git.perx.ru/perxis/perxis-go/pkg/spaces/middleware"
+	spacesTransportGrpc "git.perx.ru/perxis/perxis-go/pkg/spaces/transport/grpc"
+	"go.uber.org/zap"
+	"golang.org/x/oauth2/clientcredentials"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/credentials"
+	"google.golang.org/grpc/credentials/insecure"
+	"google.golang.org/grpc/credentials/oauth"
+	"google.golang.org/grpc/metadata"
+)
+
+const (
+	DefaultCacheSize = 1000
+	DefaultCacheTTL  = time.Second * 10
+)
+
+func NewClient(addr string, opts ...Option) (*content.Content, *grpc.ClientConn, error) {
+	ctx := context.Background()
+
+	client := &content.Content{}
+	dialOpts := make([]grpc.DialOption, 0)
+	config := &Config{}
+
+	for _, o := range opts {
+		o(config)
+	}
+
+	if config.Logger == nil {
+		config.Logger = zap.NewNop()
+	}
+
+	authDialOpts, err := config.GetAuthDialOpts(ctx)
+	if err != nil {
+		return nil, nil, err
+	}
+	dialOpts = append(dialOpts, authDialOpts...)
+
+	contentConn, err := grpc.Dial(addr, dialOpts...)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	client.Spaces = spacesTransportGrpc.NewClient(contentConn, config.ClientOptions...)
+	client.Environments = environmentsTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...)
+	client.Collections = collectionsTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...)
+	client.Items = itemsTransportGrpc.NewClient(contentConn, config.ClientOptions...)
+	client.Invitations = invitationsTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...)
+	client.Collaborators = collaboratorsTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...)
+	client.Clients = clientsTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...)
+	client.Locales = localsTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...)
+	client.Roles = rolesTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...)
+	client.References = referencesTransportGrpc.NewGRPCClient(contentConn, "", config.ClientOptions...)
+
+	if !config.NoDecode {
+		client.Items = itemsSvc.ClientEncodeMiddleware(client.Collections)(client.Items)
+		client.References = referencesSvc.ClientEncodeMiddleware(client.Collections)(client.References)
+	}
+
+	if !config.NoCache {
+		client = WithCaching(client, DefaultCacheSize, DefaultCacheTTL)
+	}
+
+	if !config.NoLog {
+		client = WithLogging(client, config.Logger, config.AccessLog)
+	}
+
+	return client, contentConn, nil
+}
+
+func WithCaching(client *content.Content, size int, ttl time.Duration) *content.Content {
+	c := *client
+
+	c.Clients = clientsSvc.CachingMiddleware(cache.NewCache(size, ttl))(client.Clients)
+	c.Environments = environmentsSvc.CachingMiddleware(cache.NewCache(size, ttl))(client.Environments)
+	c.Locales = localesSvc.CachingMiddleware(cache.NewCache(size, ttl))(client.Locales)
+	c.Roles = rolesSvc.CachingMiddleware(cache.NewCache(size, ttl))(client.Roles)
+	c.Spaces = spacesSvc.CachingMiddleware(cache.NewCache(size, ttl))(client.Spaces)
+	c.Items = itemsSvc.CachingMiddleware(cache.NewCache(size, ttl), cache.NewCache(size, ttl), c.Environments)(client.Items)
+	c.Collections = collectionsSvc.CachingMiddleware(cache.NewCache(size, ttl), c.Environments)(client.Collections)
+	c.Collaborators = collaboratorsSvc.CachingMiddleware(cache.NewCache(size, ttl))(client.Collaborators)
+	c.Invitations = invitationsSvc.CachingMiddleware(cache.NewCache(size, ttl))(client.Invitations)
+
+	return &c
+}
+
+func WithLogging(cs *content.Content, logger *zap.Logger, accessLog bool) *content.Content {
+	s := *cs
+
+	s.Collaborators = collaboratorsSvc.WithLog(s.Collaborators, logger, accessLog)
+	s.Collections = collectionsSvc.WithLog(s.Collections, logger, accessLog)
+	s.Environments = environmentsSvc.WithLog(s.Environments, logger, accessLog)
+	s.Invitations = invitationsSvc.WithLog(s.Invitations, logger, accessLog)
+	s.Items = itemsSvc.WithLog(s.Items, logger, accessLog)
+	s.Locales = localesSvc.WithLog(s.Locales, logger, accessLog)
+	s.Roles = rolesSvc.WithLog(s.Roles, logger, accessLog)
+	s.Spaces = spacesSvc.WithLog(s.Spaces, logger, accessLog)
+	s.Clients = clientsSvc.WithLog(s.Clients, logger, accessLog)
+	s.References = referencesSvc.WithLog(s.References, logger, accessLog)
+
+	return &s
+}
+
+func AddAPIKeyInterceptor(key string) grpc.UnaryClientInterceptor {
+	return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
+		ctx = metadata.AppendToOutgoingContext(ctx, "authorization", "API-Key"+" "+key)
+		return invoker(ctx, method, req, reply, cc, opts...)
+	}
+}
+
+func (c *Config) GetAuthDialOpts(ctx context.Context) (opts []grpc.DialOption, err error) {
+
+	if c.Auth == nil {
+		opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
+		return
+	}
+
+	authConfig := c.Auth
+
+	switch {
+	case authConfig.OAuth2 != nil:
+		// create external grpc client
+		conf := &clientcredentials.Config{
+			TokenURL:       authConfig.OAuth2.TokenURL,
+			ClientID:       authConfig.OAuth2.ClientID,
+			ClientSecret:   authConfig.OAuth2.ClientSecret,
+			EndpointParams: url.Values{"audience": {authConfig.OAuth2.Audience}},
+		}
+
+		// обязательно использовать tls для credentials oauth.TokenSource https://github.com/grpc/grpc-go/blob/64031cbfcf4d84c026be93ad7b74b3c290100893/credentials/oauth/oauth.go#L160
+		if authConfig.Insecure {
+			return nil, errors.New("oauth requires tls")
+		}
+		if authConfig.TLS == nil {
+			authConfig.TLS = &TLS{SkipVerify: true}
+		}
+
+		opts = append(opts,
+			grpc.WithPerRPCCredentials(oauth.TokenSource{
+				TokenSource: conf.TokenSource(ctx),
+			}),
+		)
+	case authConfig.APIKey != nil:
+
+		if !authConfig.Insecure && authConfig.TLS == nil {
+			authConfig.TLS = &TLS{SkipVerify: true}
+		}
+		opts = append(opts,
+			grpc.WithUnaryInterceptor(AddAPIKeyInterceptor(authConfig.APIKey.APIKey)),
+		)
+	}
+
+	switch {
+	case authConfig.TLS != nil && !authConfig.SkipVerify:
+		certPool := x509.NewCertPool()
+		if !certPool.AppendCertsFromPEM(authConfig.TLS.CaCert) {
+			return nil, fmt.Errorf("CA certificate not loaded")
+		}
+
+		clientCert, err := tls.X509KeyPair(authConfig.TLS.Cert, authConfig.TLS.Key)
+		if err != nil {
+			return nil, err
+		}
+
+		opts = append(opts,
+			grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
+				Certificates: []tls.Certificate{clientCert},
+				RootCAs:      certPool,
+			})),
+		)
+	case authConfig.TLS != nil && authConfig.SkipVerify:
+		opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{InsecureSkipVerify: true})))
+	default:
+		opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
+	}
+
+	return
+}
diff --git a/pkg/content/client/config.go b/pkg/content/client/config.go
new file mode 100644
index 0000000000000000000000000000000000000000..dc81c9532011730ad9d0b0c5b99a3ca86f2f591b
--- /dev/null
+++ b/pkg/content/client/config.go
@@ -0,0 +1,127 @@
+package client
+
+import (
+	kitgrpc "github.com/go-kit/kit/transport/grpc"
+	"go.uber.org/zap"
+)
+
+type Config struct {
+	Auth      *AuthConfig
+	NoCache   bool
+	NoLog     bool
+	AccessLog bool
+	Debug     bool
+	NoDecode  bool
+
+	ClientOptions []kitgrpc.ClientOption
+
+	Logger *zap.Logger
+}
+
+type AuthConfig struct {
+	*OAuth2  `json:"oauth_2,omitempty"`
+	*APIKey  `json:"api_key,omitempty"`
+	*TLS     `json:"tls,omitempty"`
+	Insecure bool `json:"insecure,omitempty"`
+}
+
+type OAuth2 struct {
+	TokenURL     string `json:"token_url,omitempty"`
+	ClientID     string `json:"client_id,omitempty"` // параметр из auth0 (клиент с таким id должен быть создан в perxis)
+	ClientSecret string `json:"client_secret,omitempty"`
+	Audience     string `json:"audience,omitempty"` // параметр из auth0 (название связанного с Application API)
+}
+
+type TLS struct {
+	CaCert     []byte `json:"tls-cacert"`
+	Cert       []byte `json:"tls-cert"`
+	Key        []byte `json:"tls-key"`
+	SkipVerify bool
+}
+
+type APIKey struct {
+	APIKey string `json:"api_key"`
+}
+
+type Option func(c *Config)
+
+func AuthOAuth2(tokenUrl, clientID, clientSecret, audience string) Option {
+	return func(c *Config) {
+		if c.Auth == nil {
+			c.Auth = &AuthConfig{}
+		}
+		c.Auth.OAuth2 = &OAuth2{
+			TokenURL:     tokenUrl,
+			ClientID:     clientID,
+			ClientSecret: clientSecret,
+			Audience:     audience,
+		}
+	}
+}
+
+func AuthTLS(cacert, cert, key []byte) Option {
+	return func(c *Config) {
+		if c.Auth == nil {
+			c.Auth = &AuthConfig{}
+		}
+		c.Auth.TLS = &TLS{
+			CaCert: cacert,
+			Cert:   cert,
+			Key:    key,
+		}
+	}
+}
+
+func AuthAPIKey(key string) Option {
+	return func(c *Config) {
+		if c.Auth == nil {
+			c.Auth = &AuthConfig{}
+		}
+		c.Auth.APIKey = &APIKey{APIKey: key}
+	}
+}
+
+func AuthInsecure() Option {
+	return func(c *Config) {
+		if c.Auth == nil {
+			c.Auth = &AuthConfig{}
+		}
+		c.Auth.Insecure = true
+	}
+}
+
+func Auth(cfg *AuthConfig) Option {
+	return func(c *Config) {
+		c.Auth = cfg
+	}
+}
+
+func NoCache() Option {
+	return func(c *Config) {
+		c.NoCache = true
+	}
+}
+
+func NoLog() Option {
+	return func(c *Config) {
+		c.NoLog = true
+	}
+}
+
+func Logger(logger *zap.Logger) Option {
+	return func(c *Config) {
+		c.Logger = logger
+	}
+}
+
+func AccessLog() Option {
+	return func(c *Config) {
+		c.AccessLog = true
+	}
+}
+
+func GrpcClientOptions(opts ...kitgrpc.ClientOption) Option {
+	return func(c *Config) {
+		c.ClientOptions = opts
+	}
+}
diff --git a/pkg/content/content.go b/pkg/content/content.go
new file mode 100644
index 0000000000000000000000000000000000000000..9a796b0dbf5f9b57a20d6a38e7282874453c118f
--- /dev/null
+++ b/pkg/content/content.go
@@ -0,0 +1,57 @@
+package content
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/auth"
+	"git.perx.ru/perxis/perxis-go/pkg/clients"
+	"git.perx.ru/perxis/perxis-go/pkg/collaborators"
+	"git.perx.ru/perxis/perxis-go/pkg/collections"
+	"git.perx.ru/perxis/perxis-go/pkg/content/versions"
+	"git.perx.ru/perxis/perxis-go/pkg/environments"
+	"git.perx.ru/perxis/perxis-go/pkg/invitations"
+	"git.perx.ru/perxis/perxis-go/pkg/items"
+	"git.perx.ru/perxis/perxis-go/pkg/locales"
+	"git.perx.ru/perxis/perxis-go/pkg/references"
+	"git.perx.ru/perxis/perxis-go/pkg/roles"
+	"git.perx.ru/perxis/perxis-go/pkg/spaces"
+)
+
+type Runnable interface {
+	Start()
+	Stop()
+}
+
+type Content struct {
+	collaborators.Collaborators
+	collections.Collections
+	environments.Environments
+	invitations.Invitations
+	items.Items
+	references.References
+	locales.Locales
+	roles.Roles
+	spaces.Spaces
+	clients.Clients
+	versions.Versions
+
+	PrincipalFactory *auth.PrincipalFactory
+
+	runners []Runnable
+}
+
+func (c *Content) RegisterStart(svc interface{}) {
+	if r, ok := svc.(Runnable); ok {
+		c.runners = append(c.runners, r)
+	}
+}
+
+func (c Content) Start() {
+	for _, r := range c.runners {
+		r.Start()
+	}
+}
+
+func (c Content) Stop() {
+	for _, r := range c.runners {
+		r.Stop()
+	}
+}
diff --git a/pkg/content/versions/mocks/Versions.go b/pkg/content/versions/mocks/Versions.go
new file mode 100644
index 0000000000000000000000000000000000000000..e4572bac6ac23903d7ad0bdd94e2f7044f9fa7e3
--- /dev/null
+++ b/pkg/content/versions/mocks/Versions.go
@@ -0,0 +1,38 @@
+// Code generated by mockery v2.7.4. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	version "git.perx.ru/perxis/perxis-go/pkg/version"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// Versions is an autogenerated mock type for the Versions type
+type Versions struct {
+	mock.Mock
+}
+
+// Get provides a mock function with given fields: ctx
+func (_m *Versions) Get(ctx context.Context) (*version.Version, error) {
+	ret := _m.Called(ctx)
+
+	var r0 *version.Version
+	if rf, ok := ret.Get(0).(func(context.Context) *version.Version); ok {
+		r0 = rf(ctx)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*version.Version)
+		}
+	}
+
+	var r1 error
+	if rf, ok := ret.Get(1).(func(context.Context) error); ok {
+		r1 = rf(ctx)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
diff --git a/pkg/content/versions/service.go b/pkg/content/versions/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..154ecdbcb423767df52d557e2bd502e3bae9206e
--- /dev/null
+++ b/pkg/content/versions/service.go
@@ -0,0 +1,14 @@
+package versions
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/pkg/version"
+)
+
+// @microgen grpc
+// @protobuf git.perx.ru/perxis/perxis-go/proto/versions/content
+// @grpc-addr content.Versions
+type Versions interface {
+	Get(ctx context.Context) (version *version.Version, err error)
+}
diff --git a/pkg/content/versions/transport/client.microgen.go b/pkg/content/versions/transport/client.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..ec9a69655fab437a4146b6cc61973197f415ff2c
--- /dev/null
+++ b/pkg/content/versions/transport/client.microgen.go
@@ -0,0 +1,24 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import (
+	"context"
+	"errors"
+
+	version "git.perx.ru/perxis/perxis-go/pkg/version"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+func (set EndpointsSet) Get(arg0 context.Context) (res0 *version.Version, res1 error) {
+	request := GetRequest{}
+	response, res1 := set.GetEndpoint(arg0, &request)
+	if res1 != nil {
+		if e, ok := status.FromError(res1); ok || e.Code() == codes.Internal || e.Code() == codes.Unknown {
+			res1 = errors.New(e.Message())
+		}
+		return
+	}
+	return response.(*GetResponse).Version, res1
+}
diff --git a/pkg/content/versions/transport/endpoints.microgen.go b/pkg/content/versions/transport/endpoints.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..b670fc2bb09ce1fe180f427ea600f703ebda02d5
--- /dev/null
+++ b/pkg/content/versions/transport/endpoints.microgen.go
@@ -0,0 +1,10 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import endpoint "github.com/go-kit/kit/endpoint"
+
+// EndpointsSet implements Versions API and used for transport purposes.
+type EndpointsSet struct {
+	GetEndpoint endpoint.Endpoint
+}
diff --git a/pkg/content/versions/transport/exchanges.microgen.go b/pkg/content/versions/transport/exchanges.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..11c9bfbda2c9d21ecd76cc67961c59f8439936c1
--- /dev/null
+++ b/pkg/content/versions/transport/exchanges.microgen.go
@@ -0,0 +1,13 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import version "git.perx.ru/perxis/perxis-go/pkg/version"
+
+type (
+	// Formal exchange type, please do not delete.
+	GetRequest  struct{}
+	GetResponse struct {
+		Version *version.Version `json:"version"`
+	}
+)
diff --git a/pkg/content/versions/transport/grpc/client.microgen.go b/pkg/content/versions/transport/grpc/client.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..830223124ccc4f41a793e4f80078bb8a5dd78ddf
--- /dev/null
+++ b/pkg/content/versions/transport/grpc/client.microgen.go
@@ -0,0 +1,23 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transportgrpc
+
+import (
+	pb "git.perx.ru/perxis/perxis-go/proto/versions/content"
+	transport "git.perx.ru/perxis/perxis-go/pkg/content/versions/transport"
+	grpckit "github.com/go-kit/kit/transport/grpc"
+	grpc "google.golang.org/grpc"
+)
+
+func NewGRPCClient(conn *grpc.ClientConn, addr string, opts ...grpckit.ClientOption) transport.EndpointsSet {
+	if addr == "" {
+		addr = "content.Versions"
+	}
+	return transport.EndpointsSet{GetEndpoint: grpckit.NewClient(
+		conn, addr, "Get",
+		_Encode_Get_Request,
+		_Decode_Get_Response,
+		pb.GetResponse{},
+		opts...,
+	).Endpoint()}
+}
diff --git a/pkg/content/versions/transport/grpc/protobuf_endpoint_converters.microgen.go b/pkg/content/versions/transport/grpc/protobuf_endpoint_converters.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..f440344087ae579ab7d8854acd856e8e3eb4a263
--- /dev/null
+++ b/pkg/content/versions/transport/grpc/protobuf_endpoint_converters.microgen.go
@@ -0,0 +1,45 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// Please, do not change functions names!
+package transportgrpc
+
+import (
+	"context"
+	"errors"
+
+	pb "git.perx.ru/perxis/perxis-go/proto/versions/content"
+	transport "git.perx.ru/perxis/perxis-go/pkg/content/versions/transport"
+	empty "github.com/golang/protobuf/ptypes/empty"
+)
+
+func _Encode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Encode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil GetResponse")
+	}
+	resp := response.(*transport.GetResponse)
+	respVersion, err := PtrVersionsVersionToProto(resp.Version)
+	if err != nil {
+		return nil, err
+	}
+	return &pb.GetResponse{Version: respVersion}, nil
+}
+
+func _Decode_Get_Request(ctx context.Context, request interface{}) (interface{}, error) {
+	return &empty.Empty{}, nil
+}
+
+func _Decode_Get_Response(ctx context.Context, response interface{}) (interface{}, error) {
+	if response == nil {
+		return nil, errors.New("nil GetResponse")
+	}
+	resp := response.(*pb.GetResponse)
+	respVersion, err := ProtoToPtrVersionsVersion(resp.Version)
+	if err != nil {
+		return nil, err
+	}
+	return &transport.GetResponse{Version: respVersion}, nil
+}
diff --git a/pkg/content/versions/transport/grpc/protobuf_type_converters.microgen.go b/pkg/content/versions/transport/grpc/protobuf_type_converters.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..87862edb1c9eab760d0d5234ffe052c847a4da3f
--- /dev/null
+++ b/pkg/content/versions/transport/grpc/protobuf_type_converters.microgen.go
@@ -0,0 +1,38 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// It is better for you if you do not change functions names!
+// This file will never be overwritten.
+package transportgrpc
+
+import (
+	"git.perx.ru/perxis/perxis-go/proto/common"
+	"git.perx.ru/perxis/perxis-go/pkg/version"
+)
+
+func PtrVersionsVersionToProto(version *version.Version) (*common.Version, error) {
+	pVersion := &common.Version{
+		ApiVersion:    version.APIVersion,
+		ServerVersion: version.ServerVersion,
+		Commit:        version.Commit,
+		BuildTime:     version.BuildTime,
+	}
+	return pVersion, nil
+}
+
+func ProtoToPtrVersionsVersion(protoVersion *common.Version) (*version.Version, error) {
+	version := &version.Version{
+		APIVersion:    protoVersion.ApiVersion,
+		ServerVersion: protoVersion.ServerVersion,
+		Commit:        protoVersion.Commit,
+		BuildTime:     protoVersion.BuildTime,
+	}
+	return version, nil
+}
+
+func PtrVersionVersionToProto(version *version.Version) (*common.Version, error) {
+	panic("function not provided") // TODO: provide converter
+}
+
+func ProtoToPtrVersionVersion(protoVersion *common.Version) (*version.Version, error) {
+	panic("function not provided") // TODO: provide converter
+}
diff --git a/pkg/content/versions/transport/grpc/server.microgen.go b/pkg/content/versions/transport/grpc/server.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..0ab4210780f7131af9c829d5b6b02bd7b46eebe4
--- /dev/null
+++ b/pkg/content/versions/transport/grpc/server.microgen.go
@@ -0,0 +1,35 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+// DO NOT EDIT.
+package transportgrpc
+
+import (
+	pb "git.perx.ru/perxis/perxis-go/proto/versions/content"
+	transport "git.perx.ru/perxis/perxis-go/pkg/content/versions/transport"
+	grpc "github.com/go-kit/kit/transport/grpc"
+	empty "github.com/golang/protobuf/ptypes/empty"
+	context "golang.org/x/net/context"
+)
+
+type versionsServer struct {
+	get grpc.Handler
+
+	pb.UnimplementedVersionsServer
+}
+
+func NewGRPCServer(endpoints *transport.EndpointsSet, opts ...grpc.ServerOption) pb.VersionsServer {
+	return &versionsServer{get: grpc.NewServer(
+		endpoints.GetEndpoint,
+		_Decode_Get_Request,
+		_Encode_Get_Response,
+		opts...,
+	)}
+}
+
+func (S *versionsServer) Get(ctx context.Context, req *empty.Empty) (*pb.GetResponse, error) {
+	_, resp, err := S.get.ServeGRPC(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return resp.(*pb.GetResponse), nil
+}
diff --git a/pkg/content/versions/transport/server.microgen.go b/pkg/content/versions/transport/server.microgen.go
new file mode 100644
index 0000000000000000000000000000000000000000..9bc6e47832efacbac2fed09a4ffe495c1bc7f266
--- /dev/null
+++ b/pkg/content/versions/transport/server.microgen.go
@@ -0,0 +1,21 @@
+// Code generated by microgen 0.9.1. DO NOT EDIT.
+
+package transport
+
+import (
+	"context"
+
+	versions "git.perx.ru/perxis/perxis-go/pkg/content/versions"
+	endpoint "github.com/go-kit/kit/endpoint"
+)
+
+func Endpoints(svc versions.Versions) EndpointsSet {
+	return EndpointsSet{GetEndpoint: GetEndpoint(svc)}
+}
+
+func GetEndpoint(svc versions.Versions) endpoint.Endpoint {
+	return func(arg0 context.Context, request interface{}) (interface{}, error) {
+		res0, res1 := svc.Get(arg0)
+		return &GetResponse{Version: res0}, res1
+	}
+}