diff --git a/Makefile b/Makefile
index 7885775e8a4b394adf66a1fbd6830eab495528ec..12b9f0834fd93f385735da38d444435d4a92d5c9 100644
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@ SHELL = bash
 
 PROTODIR=perxis-proto/proto
 DSTDIR=./proto
-ALLPROTO?=$(shell find $(PROTODIR) -name '*.proto' )
+ALLPROTO?=$(shell find -L $(PROTODIR) -name '*.proto' )
 # Убираем status.proto нужен только для front
 PROTOFILES=	$(filter-out $(PROTODIR)/status/status.proto, $(ALLPROTO))
 PROTOGOFILES=$(PROTOFILES:.proto=.pb.go)
@@ -19,7 +19,8 @@ SERVICERECOVERING=$(shell find $(PKGDIR) -name "recovering_middleware.go" -type
 
 # Генерация grpc-клиентов для go
 proto: protoc-check protoc-gen-go-check $(PROTOGOFILES)
-	@echo "Generated all protobuf Go files"
+	@echo "Generate Go from proto files"
+	@find $(PROTODIR) -name '*.proto'
 
 %.pb.go: %.proto
 	@protoc -I=$(PROTODIR) --experimental_allow_proto3_optional --go_out=$(DSTDIR) --go-grpc_out=$(DSTDIR) --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative "$<"
@@ -40,8 +41,8 @@ endif
 protoc-gen-go-check:
 ifeq (,$(wildcard $(GOPATH)/bin/protoc-gen-go))
 	$(error "Protocol Buffers Go plugin not found. \
-	Run \"go get -u google.golang.org/protobuf/cmd/protoc-gen-go google.golang.org/grpc/cmd/protoc-gen-go-grpc\" \
-	or visit \"https://github.com/golang/protobuf/tree/v1.3.2#installation\" for more.\n")
+	Run \"go install google.golang.org/protobuf/cmd/protoc-gen-go@latest google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest\" \
+	or visit \"https://grpc.io/docs/languages/go/quickstart/\" for more.\n")
 endif
 
 # Генерация логгирования (access & error) для всех сервисов. Предполагается наличие файлов `logging_middleware.go/error_middleware.go`
@@ -78,8 +79,9 @@ recovering: $(SERVICERECOVERING)
 # Генерация моков для всех интерфейсов, найденных в директории. Выходные файлы с моками сохраняются в `./mocks`
 MOCKSDIRS?=$(shell find . -name "service.go" -exec dirname {} \;)
 MOCKS=$(MOCKSDIRS:=/mocks)
+PROTOMOCKS=$(PROTODIR:=/mocks)
 
-mocks: $(MOCKS)
+mocks: $(PROTOMOCKS)
 	@echo "Generate mocks $<"
 
 %/mocks: %
diff --git a/Taskfile.yaml b/Taskfile.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8a16d687681a42527f9dca01dd01fef0790c0aca
--- /dev/null
+++ b/Taskfile.yaml
@@ -0,0 +1,48 @@
+version: '3'
+
+
+vars:
+  PROTODIR: perxis-proto/proto
+  PBDIR: pb
+
+tasks:
+  mocks:
+    deps:
+      - mocks.proto
+  mocks.proto:
+    sources:
+      - proto/**/*.proto
+    generates:
+      - proto/mocks/*.go
+    cmds:
+      - mockery --all --dir proto --output proto/mocks
+
+
+  proto:
+    sources:
+     - '{{ .PROTODIR }}/**/*.proto'
+#    generates:
+#      - '{{ .PBDIR }}/*.go'
+    ignore_error: true # Игнорировать ошибки, из-за status/status.proto
+#    silent: true
+    cmds:
+      - for: sources
+        cmd: echo {{ .ITEM }}
+#        cmd: '[ "{{.FILE}}" != "perxis-proto/proto/status/status.proto" ]'
+#          - protoc --proto_path={{ .PROTODIR }} --experimental_allow_proto3_optional --go_out={{ .PBDIR }} --go-grpc_out={{ .PBDIR }} --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative {{ .FILE }}
+
+
+  #        cmd: protoc --proto_path={{ .PROTODIR }} --experimental_allow_proto3_optional --go_out={{ .PBDIR }} --go-grpc_out={{ .PBDIR }} --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative {{ .ITEM }}
+#        task: proto_file
+#        vars:
+#          FILE: '{{ .ITEM }}'
+#        ignore_error: true
+
+
+  proto_file:
+    sources:
+      - '{{ .FILE }}'
+    cmds:
+      - '[ "{{.FILE}}" != "perxis-proto/proto/status/status.proto" ]' # Игнорировать ошибки, из-за status/status.proto
+      - protoc --proto_path={{ .PROTODIR }} --experimental_allow_proto3_optional --go_out={{ .PBDIR }} --go-grpc_out={{ .PBDIR }} --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative {{ .FILE }}
+    silent: true
\ No newline at end of file
diff --git a/pkg/clients/mocks/Clients.go b/pkg/clients/mocks/Clients.go
index f82320372fe227f84b9b5e49e306a7564aa1177f..289555adbc9e75dde72d65485e7e7961f5a12062 100644
--- a/pkg/clients/mocks/Clients.go
+++ b/pkg/clients/mocks/Clients.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -20,6 +20,10 @@ func (_m *Clients) Create(ctx context.Context, client *clients.Client) (*clients
 	ret := _m.Called(ctx, client)
 
 	var r0 *clients.Client
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.Client) (*clients.Client, error)); ok {
+		return rf(ctx, client)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *clients.Client) *clients.Client); ok {
 		r0 = rf(ctx, client)
 	} else {
@@ -28,7 +32,6 @@ func (_m *Clients) Create(ctx context.Context, client *clients.Client) (*clients
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *clients.Client) error); ok {
 		r1 = rf(ctx, client)
 	} else {
@@ -71,6 +74,10 @@ func (_m *Clients) Get(ctx context.Context, spaceId string, id string) (*clients
 	ret := _m.Called(ctx, spaceId, id)
 
 	var r0 *clients.Client
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) (*clients.Client, error)); ok {
+		return rf(ctx, spaceId, id)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string) *clients.Client); ok {
 		r0 = rf(ctx, spaceId, id)
 	} else {
@@ -79,7 +86,6 @@ func (_m *Clients) Get(ctx context.Context, spaceId string, id string) (*clients
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
 		r1 = rf(ctx, spaceId, id)
 	} else {
@@ -94,6 +100,10 @@ func (_m *Clients) GetBy(ctx context.Context, spaceId string, params *clients.Ge
 	ret := _m.Called(ctx, spaceId, params)
 
 	var r0 *clients.Client
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, *clients.GetByParams) (*clients.Client, error)); ok {
+		return rf(ctx, spaceId, params)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, *clients.GetByParams) *clients.Client); ok {
 		r0 = rf(ctx, spaceId, params)
 	} else {
@@ -102,7 +112,6 @@ func (_m *Clients) GetBy(ctx context.Context, spaceId string, params *clients.Ge
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, *clients.GetByParams) error); ok {
 		r1 = rf(ctx, spaceId, params)
 	} else {
@@ -117,6 +126,10 @@ func (_m *Clients) List(ctx context.Context, spaceId string) ([]*clients.Client,
 	ret := _m.Called(ctx, spaceId)
 
 	var r0 []*clients.Client
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) ([]*clients.Client, error)); ok {
+		return rf(ctx, spaceId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string) []*clients.Client); ok {
 		r0 = rf(ctx, spaceId)
 	} else {
@@ -125,7 +138,6 @@ func (_m *Clients) List(ctx context.Context, spaceId string) ([]*clients.Client,
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, spaceId)
 	} else {
@@ -149,13 +161,12 @@ func (_m *Clients) Update(ctx context.Context, client *clients.Client) error {
 	return r0
 }
 
-type mockConstructorTestingTNewClients interface {
+// NewClients creates a new instance of Clients. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewClients(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewClients creates a new instance of Clients. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewClients(t mockConstructorTestingTNewClients) *Clients {
+}) *Clients {
 	mock := &Clients{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/clients/mocks/Middleware.go b/pkg/clients/mocks/Middleware.go
index b3b45df720893370e26363da2e9209f59163e091..7a409ad308e232500469b0cd69589e4f3ccbe89d 100644
--- a/pkg/clients/mocks/Middleware.go
+++ b/pkg/clients/mocks/Middleware.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -29,13 +29,12 @@ func (_m *Middleware) Execute(_a0 clients.Clients) clients.Clients {
 	return r0
 }
 
-type mockConstructorTestingTNewMiddleware interface {
+// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMiddleware(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewMiddleware(t mockConstructorTestingTNewMiddleware) *Middleware {
+}) *Middleware {
 	mock := &Middleware{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/clients/mocks/Storage.go b/pkg/clients/mocks/Storage.go
index 43166e0d3f4a21bb80d5179e23d0ee87596a565e..d5cb616c077940ecdd4bfe969c98db0d974c53b2 100644
--- a/pkg/clients/mocks/Storage.go
+++ b/pkg/clients/mocks/Storage.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -22,6 +22,10 @@ func (_m *Storage) Create(ctx context.Context, client *clients.Client) (*clients
 	ret := _m.Called(ctx, client)
 
 	var r0 *clients.Client
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.Client) (*clients.Client, error)); ok {
+		return rf(ctx, client)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *clients.Client) *clients.Client); ok {
 		r0 = rf(ctx, client)
 	} else {
@@ -30,7 +34,6 @@ func (_m *Storage) Create(ctx context.Context, client *clients.Client) (*clients
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *clients.Client) error); ok {
 		r1 = rf(ctx, client)
 	} else {
@@ -59,6 +62,11 @@ func (_m *Storage) Find(ctx context.Context, spaceId string, filter *clients.Fil
 	ret := _m.Called(ctx, spaceId, filter, opts)
 
 	var r0 []*clients.Client
+	var r1 int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, *clients.Filter, *options.FindOptions) ([]*clients.Client, int, error)); ok {
+		return rf(ctx, spaceId, filter, opts)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, *clients.Filter, *options.FindOptions) []*clients.Client); ok {
 		r0 = rf(ctx, spaceId, filter, opts)
 	} else {
@@ -67,14 +75,12 @@ func (_m *Storage) Find(ctx context.Context, spaceId string, filter *clients.Fil
 		}
 	}
 
-	var r1 int
 	if rf, ok := ret.Get(1).(func(context.Context, string, *clients.Filter, *options.FindOptions) int); ok {
 		r1 = rf(ctx, spaceId, filter, opts)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	var r2 error
 	if rf, ok := ret.Get(2).(func(context.Context, string, *clients.Filter, *options.FindOptions) error); ok {
 		r2 = rf(ctx, spaceId, filter, opts)
 	} else {
@@ -117,20 +123,23 @@ func (_m *Storage) Update(ctx context.Context, upd *clients.Client) (int, int, e
 	ret := _m.Called(ctx, upd)
 
 	var r0 int
+	var r1 int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.Client) (int, int, error)); ok {
+		return rf(ctx, upd)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *clients.Client) int); ok {
 		r0 = rf(ctx, upd)
 	} else {
 		r0 = ret.Get(0).(int)
 	}
 
-	var r1 int
 	if rf, ok := ret.Get(1).(func(context.Context, *clients.Client) int); ok {
 		r1 = rf(ctx, upd)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	var r2 error
 	if rf, ok := ret.Get(2).(func(context.Context, *clients.Client) error); ok {
 		r2 = rf(ctx, upd)
 	} else {
@@ -140,13 +149,12 @@ func (_m *Storage) Update(ctx context.Context, upd *clients.Client) (int, int, e
 	return r0, r1, r2
 }
 
-type mockConstructorTestingTNewStorage interface {
+// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewStorage(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewStorage(t mockConstructorTestingTNewStorage) *Storage {
+}) *Storage {
 	mock := &Storage{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/collaborators/mocks/CollaboratorObserver.go b/pkg/collaborators/mocks/CollaboratorObserver.go
index c7f5aed4d5525f2d9136ea7645696dba9418e785..50f0d3490d114d3d99f1a20cce09df8300e5af87 100644
--- a/pkg/collaborators/mocks/CollaboratorObserver.go
+++ b/pkg/collaborators/mocks/CollaboratorObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -9,13 +9,12 @@ type CollaboratorObserver struct {
 	mock.Mock
 }
 
-type mockConstructorTestingTNewCollaboratorObserver interface {
+// NewCollaboratorObserver creates a new instance of CollaboratorObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollaboratorObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewCollaboratorObserver creates a new instance of CollaboratorObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewCollaboratorObserver(t mockConstructorTestingTNewCollaboratorObserver) *CollaboratorObserver {
+}) *CollaboratorObserver {
 	mock := &CollaboratorObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/collaborators/mocks/CollaboratorRemoveObserver.go b/pkg/collaborators/mocks/CollaboratorRemoveObserver.go
index 8733c7430a5b0a7b4c8b5d34a2a09bf90373a7c9..e2f1b0fc83c34186a3eb06bf82715d1bc44d1aa7 100644
--- a/pkg/collaborators/mocks/CollaboratorRemoveObserver.go
+++ b/pkg/collaborators/mocks/CollaboratorRemoveObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -20,13 +20,16 @@ func (_m *CollaboratorRemoveObserver) OnCollaboratorRemove(ctx context.Context,
 	ret := _m.Called(ctx, collaborator)
 
 	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.Collaborator) (string, error)); ok {
+		return rf(ctx, collaborator)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.Collaborator) string); ok {
 		r0 = rf(ctx, collaborator)
 	} else {
 		r0 = ret.Get(0).(string)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.Collaborator) error); ok {
 		r1 = rf(ctx, collaborator)
 	} else {
@@ -36,13 +39,12 @@ func (_m *CollaboratorRemoveObserver) OnCollaboratorRemove(ctx context.Context,
 	return r0, r1
 }
 
-type mockConstructorTestingTNewCollaboratorRemoveObserver interface {
+// NewCollaboratorRemoveObserver creates a new instance of CollaboratorRemoveObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollaboratorRemoveObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewCollaboratorRemoveObserver creates a new instance of CollaboratorRemoveObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewCollaboratorRemoveObserver(t mockConstructorTestingTNewCollaboratorRemoveObserver) *CollaboratorRemoveObserver {
+}) *CollaboratorRemoveObserver {
 	mock := &CollaboratorRemoveObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/collaborators/mocks/CollaboratorSetObserver.go b/pkg/collaborators/mocks/CollaboratorSetObserver.go
index 296c37f12fd206923b904386a6a15924992fe3f7..160c16b4e95c8ec7b7842b761a73ed89c1a7bc30 100644
--- a/pkg/collaborators/mocks/CollaboratorSetObserver.go
+++ b/pkg/collaborators/mocks/CollaboratorSetObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -20,13 +20,16 @@ func (_m *CollaboratorSetObserver) OnCollaboratorSet(ctx context.Context, collab
 	ret := _m.Called(ctx, collaborator)
 
 	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.Collaborator) (string, error)); ok {
+		return rf(ctx, collaborator)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.Collaborator) string); ok {
 		r0 = rf(ctx, collaborator)
 	} else {
 		r0 = ret.Get(0).(string)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.Collaborator) error); ok {
 		r1 = rf(ctx, collaborator)
 	} else {
@@ -36,13 +39,12 @@ func (_m *CollaboratorSetObserver) OnCollaboratorSet(ctx context.Context, collab
 	return r0, r1
 }
 
-type mockConstructorTestingTNewCollaboratorSetObserver interface {
+// NewCollaboratorSetObserver creates a new instance of CollaboratorSetObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollaboratorSetObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewCollaboratorSetObserver creates a new instance of CollaboratorSetObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewCollaboratorSetObserver(t mockConstructorTestingTNewCollaboratorSetObserver) *CollaboratorSetObserver {
+}) *CollaboratorSetObserver {
 	mock := &CollaboratorSetObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/collaborators/mocks/Collaborators.go b/pkg/collaborators/mocks/Collaborators.go
index c3fb5291d2d4fdb4781f951677bbc3bd0f01ac5b..079b9020adda3a36c9d877f7f662b4c9b844ac4f 100644
--- a/pkg/collaborators/mocks/Collaborators.go
+++ b/pkg/collaborators/mocks/Collaborators.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -20,13 +20,16 @@ func (_m *Collaborators) Get(ctx context.Context, spaceId string, subject string
 	ret := _m.Called(ctx, spaceId, subject)
 
 	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) (string, error)); ok {
+		return rf(ctx, spaceId, subject)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string) string); ok {
 		r0 = rf(ctx, spaceId, subject)
 	} else {
 		r0 = ret.Get(0).(string)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
 		r1 = rf(ctx, spaceId, subject)
 	} else {
@@ -41,6 +44,10 @@ func (_m *Collaborators) ListCollaborators(ctx context.Context, spaceId string)
 	ret := _m.Called(ctx, spaceId)
 
 	var r0 []*collaborators.Collaborator
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) ([]*collaborators.Collaborator, error)); ok {
+		return rf(ctx, spaceId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string) []*collaborators.Collaborator); ok {
 		r0 = rf(ctx, spaceId)
 	} else {
@@ -49,7 +56,6 @@ func (_m *Collaborators) ListCollaborators(ctx context.Context, spaceId string)
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, spaceId)
 	} else {
@@ -64,6 +70,10 @@ func (_m *Collaborators) ListSpaces(ctx context.Context, subject string) ([]*col
 	ret := _m.Called(ctx, subject)
 
 	var r0 []*collaborators.Collaborator
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) ([]*collaborators.Collaborator, error)); ok {
+		return rf(ctx, subject)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string) []*collaborators.Collaborator); ok {
 		r0 = rf(ctx, subject)
 	} else {
@@ -72,7 +82,6 @@ func (_m *Collaborators) ListSpaces(ctx context.Context, subject string) ([]*col
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, subject)
 	} else {
@@ -110,13 +119,12 @@ func (_m *Collaborators) Set(ctx context.Context, spaceId string, subject string
 	return r0
 }
 
-type mockConstructorTestingTNewCollaborators interface {
+// NewCollaborators creates a new instance of Collaborators. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollaborators(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewCollaborators creates a new instance of Collaborators. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewCollaborators(t mockConstructorTestingTNewCollaborators) *Collaborators {
+}) *Collaborators {
 	mock := &Collaborators{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/collaborators/mocks/Middleware.go b/pkg/collaborators/mocks/Middleware.go
index c3fa37385c431b4584703566c33ea6fea3e0ac95..10a10f31d239813b95778bb287595fd1cbc96051 100644
--- a/pkg/collaborators/mocks/Middleware.go
+++ b/pkg/collaborators/mocks/Middleware.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -29,13 +29,12 @@ func (_m *Middleware) Execute(_a0 collaborators.Collaborators) collaborators.Col
 	return r0
 }
 
-type mockConstructorTestingTNewMiddleware interface {
+// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMiddleware(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewMiddleware(t mockConstructorTestingTNewMiddleware) *Middleware {
+}) *Middleware {
 	mock := &Middleware{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/collaborators/mocks/Storage.go b/pkg/collaborators/mocks/Storage.go
index b2645f347aba5eeb418a5fb4228944bd051e46b0..a7ded86e00cbab6a810bc82a5f05224b150a3c79 100644
--- a/pkg/collaborators/mocks/Storage.go
+++ b/pkg/collaborators/mocks/Storage.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -22,13 +22,16 @@ func (_m *Storage) Delete(ctx context.Context, filter *collaborators.Filter) (in
 	ret := _m.Called(ctx, filter)
 
 	var r0 int
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.Filter) (int, error)); ok {
+		return rf(ctx, filter)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.Filter) int); ok {
 		r0 = rf(ctx, filter)
 	} else {
 		r0 = ret.Get(0).(int)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.Filter) error); ok {
 		r1 = rf(ctx, filter)
 	} else {
@@ -43,6 +46,10 @@ func (_m *Storage) Find(ctx context.Context, filter *collaborators.Filter, opts
 	ret := _m.Called(ctx, filter, opts)
 
 	var r0 []*collaborators.Collaborator
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.Filter, *options.FindOptions) ([]*collaborators.Collaborator, error)); ok {
+		return rf(ctx, filter, opts)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.Filter, *options.FindOptions) []*collaborators.Collaborator); ok {
 		r0 = rf(ctx, filter, opts)
 	} else {
@@ -51,7 +58,6 @@ func (_m *Storage) Find(ctx context.Context, filter *collaborators.Filter, opts
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.Filter, *options.FindOptions) error); ok {
 		r1 = rf(ctx, filter, opts)
 	} else {
@@ -103,13 +109,12 @@ func (_m *Storage) Set(ctx context.Context, collaborator *collaborators.Collabor
 	return r0
 }
 
-type mockConstructorTestingTNewStorage interface {
+// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewStorage(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewStorage(t mockConstructorTestingTNewStorage) *Storage {
+}) *Storage {
 	mock := &Storage{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/collections/mocks/CollectionCreatedObserver.go b/pkg/collections/mocks/CollectionCreatedObserver.go
new file mode 100644
index 0000000000000000000000000000000000000000..55b38993e7a3ed0d92c4d7f16047834caeb4ef3e
--- /dev/null
+++ b/pkg/collections/mocks/CollectionCreatedObserver.go
@@ -0,0 +1,54 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	collections "git.perx.ru/perxis/perxis-go/pkg/collections"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// CollectionCreatedObserver is an autogenerated mock type for the CollectionCreatedObserver type
+type CollectionCreatedObserver struct {
+	mock.Mock
+}
+
+// OnCollectionCreated provides a mock function with given fields: ctx, coll
+func (_m *CollectionCreatedObserver) OnCollectionCreated(ctx context.Context, coll *collections.Collection) (string, error) {
+	ret := _m.Called(ctx, coll)
+
+	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection) (string, error)); ok {
+		return rf(ctx, coll)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection) string); ok {
+		r0 = rf(ctx, coll)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection) error); ok {
+		r1 = rf(ctx, coll)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewCollectionCreatedObserver creates a new instance of CollectionCreatedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollectionCreatedObserver(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *CollectionCreatedObserver {
+	mock := &CollectionCreatedObserver{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/pkg/collections/mocks/CollectionDeletedObserver.go b/pkg/collections/mocks/CollectionDeletedObserver.go
new file mode 100644
index 0000000000000000000000000000000000000000..a529def57ce8131e20ad9d03fc00d4038b318fff
--- /dev/null
+++ b/pkg/collections/mocks/CollectionDeletedObserver.go
@@ -0,0 +1,54 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	collections "git.perx.ru/perxis/perxis-go/pkg/collections"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// CollectionDeletedObserver is an autogenerated mock type for the CollectionDeletedObserver type
+type CollectionDeletedObserver struct {
+	mock.Mock
+}
+
+// OnCollectionDeleted provides a mock function with given fields: ctx, coll
+func (_m *CollectionDeletedObserver) OnCollectionDeleted(ctx context.Context, coll *collections.Collection) (string, error) {
+	ret := _m.Called(ctx, coll)
+
+	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection) (string, error)); ok {
+		return rf(ctx, coll)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection) string); ok {
+		r0 = rf(ctx, coll)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection) error); ok {
+		r1 = rf(ctx, coll)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewCollectionDeletedObserver creates a new instance of CollectionDeletedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollectionDeletedObserver(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *CollectionDeletedObserver {
+	mock := &CollectionDeletedObserver{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/pkg/collections/mocks/CollectionPreUpdateObserver.go b/pkg/collections/mocks/CollectionPreUpdateObserver.go
new file mode 100644
index 0000000000000000000000000000000000000000..6d1fa2f25c1bb46e8143495a368ac44650375b79
--- /dev/null
+++ b/pkg/collections/mocks/CollectionPreUpdateObserver.go
@@ -0,0 +1,54 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	collections "git.perx.ru/perxis/perxis-go/pkg/collections"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// CollectionPreUpdateObserver is an autogenerated mock type for the CollectionPreUpdateObserver type
+type CollectionPreUpdateObserver struct {
+	mock.Mock
+}
+
+// OnCollectionPreUpdate provides a mock function with given fields: ctx, before, coll
+func (_m *CollectionPreUpdateObserver) OnCollectionPreUpdate(ctx context.Context, before *collections.Collection, coll *collections.Collection) (string, error) {
+	ret := _m.Called(ctx, before, coll)
+
+	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *collections.Collection) (string, error)); ok {
+		return rf(ctx, before, coll)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *collections.Collection) string); ok {
+		r0 = rf(ctx, before, coll)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection, *collections.Collection) error); ok {
+		r1 = rf(ctx, before, coll)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewCollectionPreUpdateObserver creates a new instance of CollectionPreUpdateObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollectionPreUpdateObserver(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *CollectionPreUpdateObserver {
+	mock := &CollectionPreUpdateObserver{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/pkg/collections/mocks/CollectionSetSchemaObserver.go b/pkg/collections/mocks/CollectionSetSchemaObserver.go
new file mode 100644
index 0000000000000000000000000000000000000000..5453aef6abf21f03075b5be16fd2e252e736d0e2
--- /dev/null
+++ b/pkg/collections/mocks/CollectionSetSchemaObserver.go
@@ -0,0 +1,54 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	collections "git.perx.ru/perxis/perxis-go/pkg/collections"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// CollectionSetSchemaObserver is an autogenerated mock type for the CollectionSetSchemaObserver type
+type CollectionSetSchemaObserver struct {
+	mock.Mock
+}
+
+// OnCollectionSetSchema provides a mock function with given fields: ctx, before, coll
+func (_m *CollectionSetSchemaObserver) OnCollectionSetSchema(ctx context.Context, before *collections.Collection, coll *collections.Collection) (string, error) {
+	ret := _m.Called(ctx, before, coll)
+
+	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *collections.Collection) (string, error)); ok {
+		return rf(ctx, before, coll)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *collections.Collection) string); ok {
+		r0 = rf(ctx, before, coll)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection, *collections.Collection) error); ok {
+		r1 = rf(ctx, before, coll)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewCollectionSetSchemaObserver creates a new instance of CollectionSetSchemaObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollectionSetSchemaObserver(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *CollectionSetSchemaObserver {
+	mock := &CollectionSetSchemaObserver{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/pkg/collections/mocks/CollectionUpdatedObserver.go b/pkg/collections/mocks/CollectionUpdatedObserver.go
new file mode 100644
index 0000000000000000000000000000000000000000..4a0dc34dd9abeab91d1e2219d75fddceecc1fa5b
--- /dev/null
+++ b/pkg/collections/mocks/CollectionUpdatedObserver.go
@@ -0,0 +1,54 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	collections "git.perx.ru/perxis/perxis-go/pkg/collections"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// CollectionUpdatedObserver is an autogenerated mock type for the CollectionUpdatedObserver type
+type CollectionUpdatedObserver struct {
+	mock.Mock
+}
+
+// OnCollectionUpdated provides a mock function with given fields: ctx, before, after
+func (_m *CollectionUpdatedObserver) OnCollectionUpdated(ctx context.Context, before *collections.Collection, after *collections.Collection) (string, error) {
+	ret := _m.Called(ctx, before, after)
+
+	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *collections.Collection) (string, error)); ok {
+		return rf(ctx, before, after)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection, *collections.Collection) string); ok {
+		r0 = rf(ctx, before, after)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection, *collections.Collection) error); ok {
+		r1 = rf(ctx, before, after)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewCollectionUpdatedObserver creates a new instance of CollectionUpdatedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollectionUpdatedObserver(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *CollectionUpdatedObserver {
+	mock := &CollectionUpdatedObserver{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/pkg/collections/mocks/Collections.go b/pkg/collections/mocks/Collections.go
index e8bf9f486093a284dcdcb9d7034cb54c8bf26996..9a0a67f6dba82b5e7343dba1f0cc152ce82acb6a 100644
--- a/pkg/collections/mocks/Collections.go
+++ b/pkg/collections/mocks/Collections.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -22,6 +22,10 @@ func (_m *Collections) Create(ctx context.Context, collection *collections.Colle
 	ret := _m.Called(ctx, collection)
 
 	var r0 *collections.Collection
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection) (*collections.Collection, error)); ok {
+		return rf(ctx, collection)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection) *collections.Collection); ok {
 		r0 = rf(ctx, collection)
 	} else {
@@ -30,7 +34,6 @@ func (_m *Collections) Create(ctx context.Context, collection *collections.Colle
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection) error); ok {
 		r1 = rf(ctx, collection)
 	} else {
@@ -66,6 +69,10 @@ func (_m *Collections) Get(ctx context.Context, spaceId string, envId string, co
 	ret := _m.Called(_ca...)
 
 	var r0 *collections.Collection
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, ...*collections.GetOptions) (*collections.Collection, error)); ok {
+		return rf(ctx, spaceId, envId, collectionId, options...)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, ...*collections.GetOptions) *collections.Collection); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId, options...)
 	} else {
@@ -74,7 +81,6 @@ func (_m *Collections) Get(ctx context.Context, spaceId string, envId string, co
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string, string, ...*collections.GetOptions) error); ok {
 		r1 = rf(ctx, spaceId, envId, collectionId, options...)
 	} else {
@@ -89,6 +95,10 @@ func (_m *Collections) List(ctx context.Context, spaceId string, envId string, f
 	ret := _m.Called(ctx, spaceId, envId, filter)
 
 	var r0 []*collections.Collection
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, *collections.Filter) ([]*collections.Collection, error)); ok {
+		return rf(ctx, spaceId, envId, filter)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, *collections.Filter) []*collections.Collection); ok {
 		r0 = rf(ctx, spaceId, envId, filter)
 	} else {
@@ -97,7 +107,6 @@ func (_m *Collections) List(ctx context.Context, spaceId string, envId string, f
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string, *collections.Filter) error); ok {
 		r1 = rf(ctx, spaceId, envId, filter)
 	} else {
@@ -149,13 +158,12 @@ func (_m *Collections) Update(ctx context.Context, coll *collections.Collection)
 	return r0
 }
 
-type mockConstructorTestingTNewCollections interface {
+// NewCollections creates a new instance of Collections. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollections(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewCollections creates a new instance of Collections. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewCollections(t mockConstructorTestingTNewCollections) *Collections {
+}) *Collections {
 	mock := &Collections{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/collections/mocks/Middleware.go b/pkg/collections/mocks/Middleware.go
index a0be631888dca02fadc204d3c256f57e9466eb5d..761a6f460bf8a54c406f7137a903782cd01b9486 100644
--- a/pkg/collections/mocks/Middleware.go
+++ b/pkg/collections/mocks/Middleware.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -29,13 +29,12 @@ func (_m *Middleware) Execute(_a0 collections.Collections) collections.Collectio
 	return r0
 }
 
-type mockConstructorTestingTNewMiddleware interface {
+// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMiddleware(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewMiddleware(t mockConstructorTestingTNewMiddleware) *Middleware {
+}) *Middleware {
 	mock := &Middleware{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/collections/mocks/Storage.go b/pkg/collections/mocks/Storage.go
index eb171b5884308f01753827496de9569a575ff818..7e4bdb4a9f9b3c7632c8157750ada779aa10223c 100644
--- a/pkg/collections/mocks/Storage.go
+++ b/pkg/collections/mocks/Storage.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -20,6 +20,10 @@ func (_m *Storage) Create(ctx context.Context, coll *collections.Collection) (*c
 	ret := _m.Called(ctx, coll)
 
 	var r0 *collections.Collection
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection) (*collections.Collection, error)); ok {
+		return rf(ctx, coll)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection) *collections.Collection); ok {
 		r0 = rf(ctx, coll)
 	} else {
@@ -28,7 +32,6 @@ func (_m *Storage) Create(ctx context.Context, coll *collections.Collection) (*c
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection) error); ok {
 		r1 = rf(ctx, coll)
 	} else {
@@ -57,6 +60,10 @@ func (_m *Storage) Get(ctx context.Context, spaceId string, envId string, collec
 	ret := _m.Called(ctx, spaceId, envId, collectionId)
 
 	var r0 *collections.Collection
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (*collections.Collection, error)); ok {
+		return rf(ctx, spaceId, envId, collectionId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string) *collections.Collection); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId)
 	} else {
@@ -65,7 +72,6 @@ func (_m *Storage) Get(ctx context.Context, spaceId string, envId string, collec
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok {
 		r1 = rf(ctx, spaceId, envId, collectionId)
 	} else {
@@ -94,6 +100,10 @@ func (_m *Storage) List(ctx context.Context, spaceId string, envId string, filte
 	ret := _m.Called(ctx, spaceId, envId, filter)
 
 	var r0 []*collections.Collection
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, *collections.Filter) ([]*collections.Collection, error)); ok {
+		return rf(ctx, spaceId, envId, filter)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, *collections.Filter) []*collections.Collection); ok {
 		r0 = rf(ctx, spaceId, envId, filter)
 	} else {
@@ -102,7 +112,6 @@ func (_m *Storage) List(ctx context.Context, spaceId string, envId string, filte
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string, *collections.Filter) error); ok {
 		r1 = rf(ctx, spaceId, envId, filter)
 	} else {
@@ -131,20 +140,23 @@ func (_m *Storage) Update(ctx context.Context, coll *collections.Collection) (in
 	ret := _m.Called(ctx, coll)
 
 	var r0 int
+	var r1 int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection) (int, int, error)); ok {
+		return rf(ctx, coll)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *collections.Collection) int); ok {
 		r0 = rf(ctx, coll)
 	} else {
 		r0 = ret.Get(0).(int)
 	}
 
-	var r1 int
 	if rf, ok := ret.Get(1).(func(context.Context, *collections.Collection) int); ok {
 		r1 = rf(ctx, coll)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	var r2 error
 	if rf, ok := ret.Get(2).(func(context.Context, *collections.Collection) error); ok {
 		r2 = rf(ctx, coll)
 	} else {
@@ -154,13 +166,12 @@ func (_m *Storage) Update(ctx context.Context, coll *collections.Collection) (in
 	return r0, r1, r2
 }
 
-type mockConstructorTestingTNewStorage interface {
+// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewStorage(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewStorage(t mockConstructorTestingTNewStorage) *Storage {
+}) *Storage {
 	mock := &Storage{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/delivery/mocks/Delivery.go b/pkg/delivery/mocks/Delivery.go
index 1f8d05f53b9b07737cee4c505069060cf3b2c647..6a10b46a01d7d3972338b26d2c82de0f46458b51 100644
--- a/pkg/delivery/mocks/Delivery.go
+++ b/pkg/delivery/mocks/Delivery.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -33,6 +33,10 @@ func (_m *Delivery) Aggregate(ctx context.Context, spaceId string, envId string,
 	ret := _m.Called(_ca...)
 
 	var r0 map[string]interface{}
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *items.Filter, ...*items.AggregatePublishedOptions) (map[string]interface{}, error)); ok {
+		return rf(ctx, spaceId, envId, collectionId, filter, options...)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *items.Filter, ...*items.AggregatePublishedOptions) map[string]interface{}); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId, filter, options...)
 	} else {
@@ -41,7 +45,6 @@ func (_m *Delivery) Aggregate(ctx context.Context, spaceId string, envId string,
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string, string, *items.Filter, ...*items.AggregatePublishedOptions) error); ok {
 		r1 = rf(ctx, spaceId, envId, collectionId, filter, options...)
 	} else {
@@ -63,6 +66,11 @@ func (_m *Delivery) FindItems(ctx context.Context, spaceId string, envId string,
 	ret := _m.Called(_ca...)
 
 	var r0 []*items.Item
+	var r1 int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *items.Filter, ...*items.FindPublishedOptions) ([]*items.Item, int, error)); ok {
+		return rf(ctx, spaceId, envId, collectionId, filter, options...)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *items.Filter, ...*items.FindPublishedOptions) []*items.Item); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId, filter, options...)
 	} else {
@@ -71,14 +79,12 @@ func (_m *Delivery) FindItems(ctx context.Context, spaceId string, envId string,
 		}
 	}
 
-	var r1 int
 	if rf, ok := ret.Get(1).(func(context.Context, string, string, string, *items.Filter, ...*items.FindPublishedOptions) int); ok {
 		r1 = rf(ctx, spaceId, envId, collectionId, filter, options...)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	var r2 error
 	if rf, ok := ret.Get(2).(func(context.Context, string, string, string, *items.Filter, ...*items.FindPublishedOptions) error); ok {
 		r2 = rf(ctx, spaceId, envId, collectionId, filter, options...)
 	} else {
@@ -93,6 +99,10 @@ func (_m *Delivery) GetCollection(ctx context.Context, spaceId string, envId str
 	ret := _m.Called(ctx, spaceId, envId, collectionId)
 
 	var r0 *collections.Collection
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (*collections.Collection, error)); ok {
+		return rf(ctx, spaceId, envId, collectionId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string) *collections.Collection); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId)
 	} else {
@@ -101,7 +111,6 @@ func (_m *Delivery) GetCollection(ctx context.Context, spaceId string, envId str
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok {
 		r1 = rf(ctx, spaceId, envId, collectionId)
 	} else {
@@ -116,6 +125,10 @@ func (_m *Delivery) GetEnvironment(ctx context.Context, spaceId string, envId st
 	ret := _m.Called(ctx, spaceId, envId)
 
 	var r0 *environments.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) (*environments.Environment, error)); ok {
+		return rf(ctx, spaceId, envId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string) *environments.Environment); ok {
 		r0 = rf(ctx, spaceId, envId)
 	} else {
@@ -124,7 +137,6 @@ func (_m *Delivery) GetEnvironment(ctx context.Context, spaceId string, envId st
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
 		r1 = rf(ctx, spaceId, envId)
 	} else {
@@ -146,6 +158,10 @@ func (_m *Delivery) GetItem(ctx context.Context, spaceId string, envId string, c
 	ret := _m.Called(_ca...)
 
 	var r0 *items.Item
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, ...*items.GetPublishedOptions) (*items.Item, error)); ok {
+		return rf(ctx, spaceId, envId, collectionId, itemId, options...)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, string, ...*items.GetPublishedOptions) *items.Item); ok {
 		r0 = rf(ctx, spaceId, envId, collectionId, itemId, options...)
 	} else {
@@ -154,7 +170,6 @@ func (_m *Delivery) GetItem(ctx context.Context, spaceId string, envId string, c
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string, string, string, ...*items.GetPublishedOptions) error); ok {
 		r1 = rf(ctx, spaceId, envId, collectionId, itemId, options...)
 	} else {
@@ -169,6 +184,10 @@ func (_m *Delivery) ListCollections(ctx context.Context, spaceId string, envId s
 	ret := _m.Called(ctx, spaceId, envId)
 
 	var r0 []*collections.Collection
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]*collections.Collection, error)); ok {
+		return rf(ctx, spaceId, envId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string) []*collections.Collection); ok {
 		r0 = rf(ctx, spaceId, envId)
 	} else {
@@ -177,7 +196,6 @@ func (_m *Delivery) ListCollections(ctx context.Context, spaceId string, envId s
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
 		r1 = rf(ctx, spaceId, envId)
 	} else {
@@ -192,6 +210,10 @@ func (_m *Delivery) ListEnvironments(ctx context.Context, spaceId string) ([]*en
 	ret := _m.Called(ctx, spaceId)
 
 	var r0 []*environments.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) ([]*environments.Environment, error)); ok {
+		return rf(ctx, spaceId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string) []*environments.Environment); ok {
 		r0 = rf(ctx, spaceId)
 	} else {
@@ -200,7 +222,6 @@ func (_m *Delivery) ListEnvironments(ctx context.Context, spaceId string) ([]*en
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, spaceId)
 	} else {
@@ -215,6 +236,10 @@ func (_m *Delivery) ListLocales(ctx context.Context, spaceId string) ([]*locales
 	ret := _m.Called(ctx, spaceId)
 
 	var r0 []*locales.Locale
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) ([]*locales.Locale, error)); ok {
+		return rf(ctx, spaceId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string) []*locales.Locale); ok {
 		r0 = rf(ctx, spaceId)
 	} else {
@@ -223,7 +248,6 @@ func (_m *Delivery) ListLocales(ctx context.Context, spaceId string) ([]*locales
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, spaceId)
 	} else {
@@ -233,13 +257,12 @@ func (_m *Delivery) ListLocales(ctx context.Context, spaceId string) ([]*locales
 	return r0, r1
 }
 
-type mockConstructorTestingTNewDelivery interface {
+// NewDelivery creates a new instance of Delivery. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewDelivery(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewDelivery creates a new instance of Delivery. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewDelivery(t mockConstructorTestingTNewDelivery) *Delivery {
+}) *Delivery {
 	mock := &Delivery{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/environments/mocks/EnvironmentConfigurationObserver.go b/pkg/environments/mocks/EnvironmentConfigurationObserver.go
index e5920c9de716195cbcc1efcd71fbcb75b91d5c40..32c8efd8b78c3e7af8e4d1d887223329fd76a43a 100644
--- a/pkg/environments/mocks/EnvironmentConfigurationObserver.go
+++ b/pkg/environments/mocks/EnvironmentConfigurationObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -28,13 +28,12 @@ func (_m *EnvironmentConfigurationObserver) OnEnvironmentConfiguration(ctx conte
 	return r0
 }
 
-type mockConstructorTestingTNewEnvironmentConfigurationObserver interface {
+// NewEnvironmentConfigurationObserver creates a new instance of EnvironmentConfigurationObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewEnvironmentConfigurationObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewEnvironmentConfigurationObserver creates a new instance of EnvironmentConfigurationObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewEnvironmentConfigurationObserver(t mockConstructorTestingTNewEnvironmentConfigurationObserver) *EnvironmentConfigurationObserver {
+}) *EnvironmentConfigurationObserver {
 	mock := &EnvironmentConfigurationObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/environments/mocks/EnvironmentCreatedObserver.go b/pkg/environments/mocks/EnvironmentCreatedObserver.go
index a2a0eb73e40ef01d0e3732a6011facd3cd4b7abe..6bed116a9a28f1ad913eed0abb0cd2f2b2931548 100644
--- a/pkg/environments/mocks/EnvironmentCreatedObserver.go
+++ b/pkg/environments/mocks/EnvironmentCreatedObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -19,13 +19,16 @@ func (_m *EnvironmentCreatedObserver) OnEnvironmentCreated(ctx context.Context,
 	ret := _m.Called(ctx, env)
 
 	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.Environment) (string, error)); ok {
+		return rf(ctx, env)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *environments.Environment) string); ok {
 		r0 = rf(ctx, env)
 	} else {
 		r0 = ret.Get(0).(string)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *environments.Environment) error); ok {
 		r1 = rf(ctx, env)
 	} else {
@@ -35,13 +38,12 @@ func (_m *EnvironmentCreatedObserver) OnEnvironmentCreated(ctx context.Context,
 	return r0, r1
 }
 
-type mockConstructorTestingTNewEnvironmentCreatedObserver interface {
+// NewEnvironmentCreatedObserver creates a new instance of EnvironmentCreatedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewEnvironmentCreatedObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewEnvironmentCreatedObserver creates a new instance of EnvironmentCreatedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewEnvironmentCreatedObserver(t mockConstructorTestingTNewEnvironmentCreatedObserver) *EnvironmentCreatedObserver {
+}) *EnvironmentCreatedObserver {
 	mock := &EnvironmentCreatedObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/environments/mocks/EnvironmentDeletedObserver.go b/pkg/environments/mocks/EnvironmentDeletedObserver.go
index 852cec94086338d53518afff08d18764b88e5170..1a101f04be093b1b48e89d8173c969beebb66754 100644
--- a/pkg/environments/mocks/EnvironmentDeletedObserver.go
+++ b/pkg/environments/mocks/EnvironmentDeletedObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -28,13 +28,12 @@ func (_m *EnvironmentDeletedObserver) OnEnvironmentDeleted(ctx context.Context,
 	return r0
 }
 
-type mockConstructorTestingTNewEnvironmentDeletedObserver interface {
+// NewEnvironmentDeletedObserver creates a new instance of EnvironmentDeletedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewEnvironmentDeletedObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewEnvironmentDeletedObserver creates a new instance of EnvironmentDeletedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewEnvironmentDeletedObserver(t mockConstructorTestingTNewEnvironmentDeletedObserver) *EnvironmentDeletedObserver {
+}) *EnvironmentDeletedObserver {
 	mock := &EnvironmentDeletedObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/environments/mocks/EnvironmentMigratedObserver.go b/pkg/environments/mocks/EnvironmentMigratedObserver.go
index a2f042880ffe88d86187263d1688596d9a878507..c529a8b81f9caecbb4d78b4d10079f406dc88bf6 100644
--- a/pkg/environments/mocks/EnvironmentMigratedObserver.go
+++ b/pkg/environments/mocks/EnvironmentMigratedObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -28,13 +28,12 @@ func (_m *EnvironmentMigratedObserver) OnEnvironmentMigrated(ctx context.Context
 	return r0
 }
 
-type mockConstructorTestingTNewEnvironmentMigratedObserver interface {
+// NewEnvironmentMigratedObserver creates a new instance of EnvironmentMigratedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewEnvironmentMigratedObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewEnvironmentMigratedObserver creates a new instance of EnvironmentMigratedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewEnvironmentMigratedObserver(t mockConstructorTestingTNewEnvironmentMigratedObserver) *EnvironmentMigratedObserver {
+}) *EnvironmentMigratedObserver {
 	mock := &EnvironmentMigratedObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/environments/mocks/EnvironmentObserver.go b/pkg/environments/mocks/EnvironmentObserver.go
index 309d0ed217317c53be90e4f6a43afcddc3587eb4..15f243d920ce58b8d770d742fa778bd4ca9a7133 100644
--- a/pkg/environments/mocks/EnvironmentObserver.go
+++ b/pkg/environments/mocks/EnvironmentObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -9,13 +9,12 @@ type EnvironmentObserver struct {
 	mock.Mock
 }
 
-type mockConstructorTestingTNewEnvironmentObserver interface {
+// NewEnvironmentObserver creates a new instance of EnvironmentObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewEnvironmentObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewEnvironmentObserver creates a new instance of EnvironmentObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewEnvironmentObserver(t mockConstructorTestingTNewEnvironmentObserver) *EnvironmentObserver {
+}) *EnvironmentObserver {
 	mock := &EnvironmentObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/environments/mocks/EnvironmentUpdatedObserver.go b/pkg/environments/mocks/EnvironmentUpdatedObserver.go
index 4a7e76673439dcf09d9b3b52c461482e637960a3..75073188aeb89593bf12555d9ada3a7cc9ec7837 100644
--- a/pkg/environments/mocks/EnvironmentUpdatedObserver.go
+++ b/pkg/environments/mocks/EnvironmentUpdatedObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -28,13 +28,12 @@ func (_m *EnvironmentUpdatedObserver) OnEnvironmentUpdated(ctx context.Context,
 	return r0
 }
 
-type mockConstructorTestingTNewEnvironmentUpdatedObserver interface {
+// NewEnvironmentUpdatedObserver creates a new instance of EnvironmentUpdatedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewEnvironmentUpdatedObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewEnvironmentUpdatedObserver creates a new instance of EnvironmentUpdatedObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewEnvironmentUpdatedObserver(t mockConstructorTestingTNewEnvironmentUpdatedObserver) *EnvironmentUpdatedObserver {
+}) *EnvironmentUpdatedObserver {
 	mock := &EnvironmentUpdatedObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/environments/mocks/Environments.go b/pkg/environments/mocks/Environments.go
index cfd1ef59a95c18881afa675ef92a12d3bf455fd7..22da5d024c9cd43506a35e6e11a264fff9189a54 100644
--- a/pkg/environments/mocks/Environments.go
+++ b/pkg/environments/mocks/Environments.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -19,6 +19,10 @@ func (_m *Environments) Create(ctx context.Context, env *environments.Environmen
 	ret := _m.Called(ctx, env)
 
 	var r0 *environments.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.Environment) (*environments.Environment, error)); ok {
+		return rf(ctx, env)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *environments.Environment) *environments.Environment); ok {
 		r0 = rf(ctx, env)
 	} else {
@@ -27,7 +31,6 @@ func (_m *Environments) Create(ctx context.Context, env *environments.Environmen
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *environments.Environment) error); ok {
 		r1 = rf(ctx, env)
 	} else {
@@ -56,6 +59,10 @@ func (_m *Environments) Get(ctx context.Context, spaceId string, envId string) (
 	ret := _m.Called(ctx, spaceId, envId)
 
 	var r0 *environments.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) (*environments.Environment, error)); ok {
+		return rf(ctx, spaceId, envId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string) *environments.Environment); ok {
 		r0 = rf(ctx, spaceId, envId)
 	} else {
@@ -64,7 +71,6 @@ func (_m *Environments) Get(ctx context.Context, spaceId string, envId string) (
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
 		r1 = rf(ctx, spaceId, envId)
 	} else {
@@ -79,6 +85,10 @@ func (_m *Environments) List(ctx context.Context, spaceId string) ([]*environmen
 	ret := _m.Called(ctx, spaceId)
 
 	var r0 []*environments.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) ([]*environments.Environment, error)); ok {
+		return rf(ctx, spaceId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string) []*environments.Environment); ok {
 		r0 = rf(ctx, spaceId)
 	} else {
@@ -87,7 +97,6 @@ func (_m *Environments) List(ctx context.Context, spaceId string) ([]*environmen
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, spaceId)
 	} else {
@@ -160,13 +169,12 @@ func (_m *Environments) Update(ctx context.Context, env *environments.Environmen
 	return r0
 }
 
-type mockConstructorTestingTNewEnvironments interface {
+// NewEnvironments creates a new instance of Environments. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewEnvironments(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewEnvironments creates a new instance of Environments. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewEnvironments(t mockConstructorTestingTNewEnvironments) *Environments {
+}) *Environments {
 	mock := &Environments{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/environments/mocks/Middleware.go b/pkg/environments/mocks/Middleware.go
index 3620aacf4a16f9ecf159bd56508e7db27f72625d..e88e67e9c4b541450200ab8cf9f313383366d44b 100644
--- a/pkg/environments/mocks/Middleware.go
+++ b/pkg/environments/mocks/Middleware.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -29,13 +29,12 @@ func (_m *Middleware) Execute(_a0 environments.Environments) environments.Enviro
 	return r0
 }
 
-type mockConstructorTestingTNewMiddleware interface {
+// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMiddleware(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewMiddleware(t mockConstructorTestingTNewMiddleware) *Middleware {
+}) *Middleware {
 	mock := &Middleware{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/environments/mocks/Storage.go b/pkg/environments/mocks/Storage.go
index 68411694c5fc03e497e8c37e6a1ff0a1f1a68721..37b8f6e5ef7349b4739969b8bba457979a8faad0 100644
--- a/pkg/environments/mocks/Storage.go
+++ b/pkg/environments/mocks/Storage.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -19,6 +19,10 @@ func (_m *Storage) Create(ctx context.Context, create *environments.Environment)
 	ret := _m.Called(ctx, create)
 
 	var r0 *environments.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.Environment) (*environments.Environment, error)); ok {
+		return rf(ctx, create)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *environments.Environment) *environments.Environment); ok {
 		r0 = rf(ctx, create)
 	} else {
@@ -27,7 +31,6 @@ func (_m *Storage) Create(ctx context.Context, create *environments.Environment)
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *environments.Environment) error); ok {
 		r1 = rf(ctx, create)
 	} else {
@@ -56,6 +59,10 @@ func (_m *Storage) Get(ctx context.Context, spaceId string, envId string) (*envi
 	ret := _m.Called(ctx, spaceId, envId)
 
 	var r0 *environments.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) (*environments.Environment, error)); ok {
+		return rf(ctx, spaceId, envId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string) *environments.Environment); ok {
 		r0 = rf(ctx, spaceId, envId)
 	} else {
@@ -64,7 +71,6 @@ func (_m *Storage) Get(ctx context.Context, spaceId string, envId string) (*envi
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
 		r1 = rf(ctx, spaceId, envId)
 	} else {
@@ -93,6 +99,10 @@ func (_m *Storage) List(ctx context.Context, spaceId string) ([]*environments.En
 	ret := _m.Called(ctx, spaceId)
 
 	var r0 []*environments.Environment
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) ([]*environments.Environment, error)); ok {
+		return rf(ctx, spaceId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string) []*environments.Environment); ok {
 		r0 = rf(ctx, spaceId)
 	} else {
@@ -101,7 +111,6 @@ func (_m *Storage) List(ctx context.Context, spaceId string) ([]*environments.En
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, spaceId)
 	} else {
@@ -156,20 +165,23 @@ func (_m *Storage) Update(ctx context.Context, up *environments.Environment, opt
 	ret := _m.Called(_ca...)
 
 	var r0 int
+	var r1 int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.Environment, ...*environments.UpdateOptions) (int, int, error)); ok {
+		return rf(ctx, up, options...)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *environments.Environment, ...*environments.UpdateOptions) int); ok {
 		r0 = rf(ctx, up, options...)
 	} else {
 		r0 = ret.Get(0).(int)
 	}
 
-	var r1 int
 	if rf, ok := ret.Get(1).(func(context.Context, *environments.Environment, ...*environments.UpdateOptions) int); ok {
 		r1 = rf(ctx, up, options...)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	var r2 error
 	if rf, ok := ret.Get(2).(func(context.Context, *environments.Environment, ...*environments.UpdateOptions) error); ok {
 		r2 = rf(ctx, up, options...)
 	} else {
@@ -179,13 +191,12 @@ func (_m *Storage) Update(ctx context.Context, up *environments.Environment, opt
 	return r0, r1, r2
 }
 
-type mockConstructorTestingTNewStorage interface {
+// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewStorage(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewStorage(t mockConstructorTestingTNewStorage) *Storage {
+}) *Storage {
 	mock := &Storage{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/errors/grpc/convert.go b/pkg/errors/grpc/convert.go
new file mode 100644
index 0000000000000000000000000000000000000000..a32cc55bcd6416aff02352f70457755a533133c7
--- /dev/null
+++ b/pkg/errors/grpc/convert.go
@@ -0,0 +1,148 @@
+package grpc
+
+import (
+	"fmt"
+
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/proto/common"
+	"github.com/hashicorp/go-multierror"
+)
+
+func ErrorFromProto(err error, pberr *common.Error) error {
+	if pberr == nil {
+		return nil
+	}
+
+	if err == nil {
+		err = errors.New(pberr.Message)
+	}
+
+	// Должен быть первым в цепочке
+	err = badRequestFromPb(err, pberr)
+
+	if pberr.ErrorCode != 0 {
+		err = errors.WithCode(err, pberr.ErrorCode)
+	}
+
+	if pberr.ErrorId != "" {
+		err = errors.SetID(err, pberr.ErrorId)
+	}
+
+	if pberr.Domain != "" {
+		err = errors.WithDomain(err, pberr.Domain)
+	}
+
+	if pberr.Details != "" {
+		err = errors.WithDetail(err, pberr.Details)
+	}
+
+	err = helpFromPb(err, pberr)
+
+	var errs []error
+	for _, e := range pberr.Errors {
+		errs = append(errs, ErrorFromProto(nil, e))
+	}
+	if len(errs) > 0 {
+		err = errors.WithErrors(err, errs...)
+	}
+
+	return err
+}
+
+func ProtoFromError(err error) *common.Error {
+	if err == nil {
+		return nil
+	}
+
+	err = errors.WithID(err)
+	pberr := &common.Error{
+		ErrorCode:         errors.GetCode(err),
+		ErrorId:           errors.GetID(err),
+		Reason:            "", // TBD
+		Domain:            errors.GetDomain(err),
+		Message:           err.Error(),
+		Details:           errors.GetDetail(err),
+		Metadata:          nil, // TBD
+		BadRequest:        getBadRequest(err),
+		DebugInfo:         getDebugInfo(err),
+		Help:              getHelp(err),
+		LocalizedMessages: nil, // TBD
+		Errors:            nil,
+	}
+
+	for _, e := range errors.GetErrors(err) {
+		pberr.Errors = append(pberr.Errors, ProtoFromError(e))
+	}
+
+	return pberr
+}
+
+func getHelp(err error) *common.Error_Help {
+	if help := errors.GetHelp(err); help != nil {
+		pbhelp := &common.Error_Help{}
+		for _, hl := range help.Links {
+			pbhelp.Links = append(pbhelp.Links, &common.Error_Help_Link{Description: hl.Description, Url: hl.URL})
+		}
+		return pbhelp
+	}
+	return nil
+}
+
+func helpFromPb(err error, pberr *common.Error) error {
+	if pberr.Help == nil || len(pberr.Help.Links) == 0 {
+		return err
+	}
+	h := &errors.Help{}
+	for _, l := range pberr.Help.Links {
+		h.Links = append(h.Links, errors.HelpLink{Description: l.Description, URL: l.Url})
+	}
+	return errors.WithHelp(err, h)
+}
+
+func getBadRequest(err error) *common.Error_BadRequest {
+	br := &common.Error_BadRequest{}
+
+	var merr *multierror.Error
+	if errors.As(err, &merr) {
+		for _, e := range merr.Errors {
+			var errField errors.FieldError
+			if errors.As(e, &errField) {
+				br.Errors = append(br.Errors, &common.Error_BadRequest_FieldViolation{Field: errField.Field(), Description: errors.Unwrap(errField).Error()})
+			}
+		}
+	} else {
+		var errField errors.FieldError
+		if errors.As(err, &errField) {
+			br.Errors = append(br.Errors, &common.Error_BadRequest_FieldViolation{Field: errField.Field(), Description: errors.Unwrap(errField).Error()})
+		}
+	}
+
+	if len(br.Errors) > 0 {
+		return br
+	}
+
+	return nil
+}
+
+func getDebugInfo(err error) *common.Error_DebugInfo {
+	if trace, ok := errors.GetStackTrace(err); ok {
+		di := &common.Error_DebugInfo{}
+		for _, t := range trace {
+			di.StackTrace = append(di.StackTrace, fmt.Sprintf("%+v", t))
+		}
+		return di
+	}
+
+	return nil
+}
+
+func badRequestFromPb(err error, pberr *common.Error) error {
+	if pberr.BadRequest == nil || len(pberr.BadRequest.Errors) == 0 {
+		return err
+	}
+	var merr error
+	for _, e := range pberr.BadRequest.Errors {
+		merr = multierror.Append(merr, errors.WithField(errors.New(e.Description), e.Field))
+	}
+	return errors.WrapErr(err, merr)
+}
diff --git a/pkg/errors/grpc/status_test.go b/pkg/errors/grpc/convert_test.go
similarity index 52%
rename from pkg/errors/grpc/status_test.go
rename to pkg/errors/grpc/convert_test.go
index 4a36f34fdb5239d9358fb05e0b345d4288cae047..7eeac21669d7563a8186530f137912ee9737cb3e 100644
--- a/pkg/errors/grpc/status_test.go
+++ b/pkg/errors/grpc/convert_test.go
@@ -7,6 +7,8 @@ import (
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	"git.perx.ru/perxis/perxis-go/proto/common"
 	"github.com/hashicorp/go-multierror"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
 func Test_getBadRequest(t *testing.T) {
@@ -60,3 +62,84 @@ func Test_getHelp(t *testing.T) {
 		})
 	}
 }
+
+func TestProtoFromError(t *testing.T) {
+	err := errors.New("some error")
+	err = errors.SetID(err, "0")
+	err = errors.WithDetail(err, "Ошибка")
+	err = errors.WithDomain(err, "domain-1")
+	err = errors.WithHelp(err, &errors.Help{Links: []errors.HelpLink{{Description: "Docs", URL: "https://docs.com"}}})
+	err = errors.WithErrors(err,
+		errors.SetID(errors.WithDetail(errors.New("err-1"), "Ошибка-1"), "1"),
+		errors.SetID(errors.WithDetail(errors.New("err-2"), "Ошибка-2"), "2"),
+		errors.SetID(errors.WithDetail(errors.New("err-3"), "Ошибка-3"), "3"),
+	)
+
+	assert.Equal(t,
+		&common.Error{
+			ErrorCode: 0,
+			ErrorId:   "0",
+			Reason:    "",
+			Domain:    "domain-1",
+			Message:   "some error",
+			Details:   "Ошибка",
+			Help: &common.Error_Help{
+				Links: []*common.Error_Help_Link{{
+					Description: "Docs",
+					Url:         "https://docs.com",
+				}},
+			},
+			Errors: []*common.Error{
+				{ErrorId: "1", Message: "err-1", Details: "Ошибка-1"},
+				{ErrorId: "2", Message: "err-2", Details: "Ошибка-2"},
+				{ErrorId: "3", Message: "err-3", Details: "Ошибка-3"},
+			},
+		},
+		ProtoFromError(err),
+	)
+}
+
+func TestErrorFromProto(t *testing.T) {
+	pberr := &common.Error{
+		ErrorCode: 0,
+		ErrorId:   "0",
+		Reason:    "",
+		Domain:    "domain-1",
+		Message:   "some error",
+		Details:   "Ошибка",
+		Help: &common.Error_Help{
+			Links: []*common.Error_Help_Link{{
+				Description: "Docs",
+				Url:         "https://docs.com",
+			}},
+		},
+		Errors: []*common.Error{
+			{ErrorId: "1", Message: "err-1", Details: "Ошибка-1"},
+			{ErrorId: "2", Message: "err-2", Details: "Ошибка-2"},
+			{ErrorId: "3", Message: "err-3", Details: "Ошибка-3"},
+		},
+	}
+
+	err := ErrorFromProto(nil, pberr)
+	require.NotNil(t, err)
+	assert.Equal(t, "0", errors.GetID(err))
+	assert.Equal(t, "some error", err.Error())
+	assert.Equal(t, "Ошибка", errors.GetDetail(err))
+	assert.Equal(t, "domain-1", errors.GetDomain(err))
+	assert.Equal(t, &errors.Help{Links: []errors.HelpLink{{Description: "Docs", URL: "https://docs.com"}}}, errors.GetHelp(err))
+
+	errs := errors.GetErrors(err)
+	require.Len(t, errs, 3)
+
+	assert.Equal(t, "1", errors.GetID(errs[0]))
+	assert.Equal(t, "err-1", errs[0].Error())
+	assert.Equal(t, "Ошибка-1", errors.GetDetail(errs[0]))
+
+	assert.Equal(t, "2", errors.GetID(errs[1]))
+	assert.Equal(t, "err-2", errs[1].Error())
+	assert.Equal(t, "Ошибка-2", errors.GetDetail(errs[1]))
+
+	assert.Equal(t, "3", errors.GetID(errs[2]))
+	assert.Equal(t, "err-3", errs[2].Error())
+	assert.Equal(t, "Ошибка-3", errors.GetDetail(errs[2]))
+}
diff --git a/pkg/errors/grpc/status.go b/pkg/errors/grpc/status.go
index e9cc9ee08d1e962e62da1b3d9a9407bc02b3fc2d..2159748874bc9fbd01c4e7b0c91a4910b1e10604 100644
--- a/pkg/errors/grpc/status.go
+++ b/pkg/errors/grpc/status.go
@@ -1,136 +1,27 @@
 package grpc
 
 import (
-	"fmt"
-
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	"git.perx.ru/perxis/perxis-go/proto/common"
-	"github.com/hashicorp/go-multierror"
 	"google.golang.org/grpc/status"
 )
 
-func getHelp(err error) *common.Error_Help {
-	if help := errors.GetHelp(err); help != nil {
-		pbhelp := &common.Error_Help{}
-		for _, hl := range help.Links {
-			pbhelp.Links = append(pbhelp.Links, &common.Error_Help_Link{Description: hl.Description, Url: hl.URL})
-		}
-		return pbhelp
-	}
-	return nil
-}
-
-func helpFromPb(err error, pberr *common.Error) error {
-	if pberr.Help == nil || len(pberr.Help.Links) == 0 {
-		return err
-	}
-	h := &errors.Help{}
-	for _, l := range pberr.Help.Links {
-		h.Links = append(h.Links, errors.HelpLink{Description: l.Description, URL: l.Url})
-	}
-	return errors.WithHelp(err, h)
-}
-
-func getBadRequest(err error) *common.Error_BadRequest {
-	br := &common.Error_BadRequest{}
-
-	var merr *multierror.Error
-	if errors.As(err, &merr) {
-		for _, e := range merr.Errors {
-			var errField errors.FieldError
-			if errors.As(e, &errField) {
-				br.Errors = append(br.Errors, &common.Error_BadRequest_FieldViolation{Field: errField.Field(), Description: errors.Unwrap(errField).Error()})
-			}
-		}
-	} else {
-		var errField errors.FieldError
-		if errors.As(err, &errField) {
-			br.Errors = append(br.Errors, &common.Error_BadRequest_FieldViolation{Field: errField.Field(), Description: errors.Unwrap(errField).Error()})
-		}
-	}
-
-	if len(br.Errors) > 0 {
-		return br
-	}
-
-	return nil
-}
-
-func getDebugInfo(err error) *common.Error_DebugInfo {
-	if trace, ok := errors.GetStackTrace(err); ok {
-		di := &common.Error_DebugInfo{}
-		for _, t := range trace {
-			di.StackTrace = append(di.StackTrace, fmt.Sprintf("%+v", t))
-		}
-		return di
-	}
-
-	return nil
-}
-
-func badRequestFromPb(err error, pberr *common.Error) error {
-	if pberr.BadRequest == nil || len(pberr.BadRequest.Errors) == 0 {
-		return err
-	}
-	var merr error
-	for _, e := range pberr.BadRequest.Errors {
-		merr = multierror.Append(merr, errors.WithField(errors.New(e.Description), e.Field))
-	}
-	return errors.WrapErr(err, merr)
-}
-
 func StatusFromError(err error) *status.Status {
 	if err == nil {
 		return nil
 	}
-	err = errors.WithID(err)
-	pberr := &common.Error{
-		ErrorCode:         errors.GetCode(err),
-		ErrorId:           errors.GetID(err),
-		Reason:            "", // TBD
-		Domain:            errors.GetDomain(err),
-		Help:              getHelp(err),
-		Metadata:          nil, // TBD
-		BadRequest:        getBadRequest(err),
-		DebugInfo:         getDebugInfo(err),
-		LocalizedMessages: nil, // TBD
-	}
-
+	pberr := ProtoFromError(err)
 	st := status.New(errors.GetStatusCode(err), err.Error())
 	st, _ = st.WithDetails(pberr)
 	return st
 }
 
-func convertProtoError(err error, pberr *common.Error) error {
-	if pberr == nil {
-		return nil
-	}
-
-	// Должен быть первым в цепочке
-	err = badRequestFromPb(err, pberr)
-
-	if pberr.ErrorCode != 0 {
-		err = errors.WithCode(err, pberr.ErrorCode)
-	}
-
-	if pberr.ErrorId != "" {
-		err = errors.SetID(err, pberr.ErrorId)
-	}
-
-	if pberr.Domain != "" {
-		err = errors.WithDomain(err, pberr.Domain)
-	}
-
-	err = helpFromPb(err, pberr)
-	return err
-}
-
 func ErrorFromStatus(st *status.Status) error {
 	details := st.Details()
 	err := errors.New(st.Message())
 	if len(details) > 0 {
 		if pberr, ok := details[0].(*common.Error); ok {
-			err = convertProtoError(err, pberr)
+			err = ErrorFromProto(err, pberr)
 		}
 	}
 	err = errors.WithStatusCode(err, st.Code())
diff --git a/pkg/extension/action.go b/pkg/extension/action.go
index 9e4247c03e42e743ddd3710dd916d1a39635c52d..d4839a8adf4698545007e296fc0c17d311de8969 100644
--- a/pkg/extension/action.go
+++ b/pkg/extension/action.go
@@ -6,11 +6,6 @@ import (
 	"github.com/mitchellh/mapstructure"
 )
 
-const (
-	ActionsCollectionID   = "space_actions"
-	ActionsCollectionName = "Настройки/Действия"
-)
-
 type (
 	ActionKind    = pb.Action_Kind
 	ResponseState = pb.ActionResponse_State
diff --git a/pkg/extension/client.go b/pkg/extension/client.go
index fc7276552f11ff086934e8d524541c77249d6d22..3f8d8bcb02427cddf7ccd8af2279924c091e3a21 100644
--- a/pkg/extension/client.go
+++ b/pkg/extension/client.go
@@ -3,18 +3,21 @@ package extension
 import (
 	"context"
 
-	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/pkg/operation"
 	pb "git.perx.ru/perxis/perxis-go/proto/extensions"
 	"google.golang.org/grpc"
 )
 
+// Client описывает клиент для расширений
 type Client struct {
-	client pb.ExtensionClient
+	client    pb.ExtensionServiceClient
+	opsClient operation.Client
 }
 
 func NewClient(conn *grpc.ClientConn) *Client {
 	return &Client{
-		client: pb.NewExtensionClient(conn),
+		client:    pb.NewExtensionServiceClient(conn),
+		opsClient: operation.NewClient(conn),
 	}
 }
 
@@ -22,61 +25,64 @@ func (c Client) GetDescriptor() *ExtensionDescriptor {
 	return nil
 }
 
-type results interface {
-	GetResults() []*RequestResult
+func (c *Client) InstallOperation(ctx context.Context, req *InstallRequest) (*operation.Operation, error) {
+	pbOperation, err := c.client.Install(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return operation.Wrap(c.opsClient, pbOperation), nil
 }
 
-func getErrors(out results, err, wrapErr error) error {
+func (c *Client) Install(ctx context.Context, req *InstallRequest) error {
+	op, err := c.InstallOperation(ctx, req)
 	if err != nil {
 		return err
 	}
-
-	res := out.GetResults()
-
-	var errs []error
-	for _, r := range res {
-		if r.State == RequestError {
-			err := errors.New(r.GetError())
-			if msg := r.GetMsg(); msg != "" {
-				err = errors.WithDetail(err, msg)
-			}
-			errs = append(errs, ExtensionError(err, r.Extension))
-		}
-
-		if r.UpdateAvailable {
-			errs = append(errs, ExtensionError(ErrUpdateAvailable, r.Extension))
-		}
-	}
-
-	if len(errs) > 0 {
-		return errors.WithErrors(wrapErr, errs...)
+	if err := op.Wait(ctx); err != nil {
+		return err
 	}
-
-	return nil
+	return op.Error()
 }
 
-func (c Client) Install(ctx context.Context, in *InstallRequest) error {
-	out, err := c.client.Install(ctx, in)
-	return getErrors(out, err, ErrInstall)
+func (c *Client) UninstallOperation(ctx context.Context, req *UninstallRequest) (*operation.Operation, error) {
+	pbOperation, err := c.client.Uninstall(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return operation.Wrap(c.opsClient, pbOperation), nil
 }
 
-func (c Client) Check(ctx context.Context, in *CheckRequest) error {
-	out, err := c.client.Check(ctx, in)
-	return getErrors(out, err, ErrCheck)
+func (c *Client) Uninstall(ctx context.Context, req *UninstallRequest) error {
+	op, err := c.UninstallOperation(ctx, req)
+	if err != nil {
+		return err
+	}
+	if err := op.Wait(ctx); err != nil {
+		return err
+	}
+	return op.Error()
 }
 
-func (c Client) Update(ctx context.Context, in *UpdateRequest) error {
-	out, err := c.client.Update(ctx, in)
-	return getErrors(out, err, ErrUpdate)
+func (c *Client) CheckOperation(ctx context.Context, req *CheckRequest) (*operation.Operation, error) {
+	pbOperation, err := c.client.Check(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	return operation.Wrap(c.opsClient, pbOperation), nil
 }
 
-func (c Client) Uninstall(ctx context.Context, in *UninstallRequest) error {
-	out, err := c.client.Uninstall(ctx, in)
-	return getErrors(out, err, ErrUninstall)
+func (c *Client) Check(ctx context.Context, req *CheckRequest) error {
+	op, err := c.CheckOperation(ctx, req)
+	if err != nil {
+		return err
+	}
+	if err := op.Wait(ctx); err != nil {
+		return err
+	}
+	return op.Error()
 }
 
 func (c Client) Action(ctx context.Context, in *ActionRequest) (*ActionResponse, error) {
 	out, err := c.client.Action(ctx, in)
 	return out, err
-	//return ActionResponseFromPB(out), err
 }
diff --git a/pkg/extension/client_test.go b/pkg/extension/client_test.go
deleted file mode 100644
index d00032a5cb5f944a8dc4a3b6114d1fffaf14dbf7..0000000000000000000000000000000000000000
--- a/pkg/extension/client_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package extension
-
-import (
-	"testing"
-
-	"git.perx.ru/perxis/perxis-go/pkg/errors"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-func TestGetErrors(t *testing.T) {
-
-	getOut := func(results ...*RequestResult) results { return &InstallResponse{Results: results} }
-
-	tests := []struct {
-		name     string
-		out      results
-		err      error
-		wrapErr  error
-		checkErr func(t *testing.T, err error)
-	}{
-		{
-			name: "no errors",
-			out:  getOut(&RequestResult{State: RequestOK, Extension: "a"}),
-		},
-		{
-			name:    "no error",
-			out:     getOut(&RequestResult{State: RequestOK, Extension: "a"}),
-			wrapErr: ErrInstall,
-		},
-		{
-			name:    "one errored result",
-			out:     getOut(&RequestResult{State: RequestError, Extension: "a", Error: "some err", Msg: "Ошибка"}),
-			wrapErr: ErrInstall,
-			checkErr: func(t *testing.T, err error) {
-				assert.ErrorIs(t, err, ErrInstall)
-				errs := errors.GetErrors(err)
-				require.Len(t, errs, 1)
-
-				extErr := errs[0]
-				assert.Equal(t, "a", ExtensionFromError(extErr))
-				assert.Equal(t, "some err", extErr.Error())
-				assert.Equal(t, "Ошибка", errors.GetDetail(extErr))
-			},
-		},
-		{
-			name: "multiple results, some of them errored",
-			out: getOut(
-				&RequestResult{State: RequestError, Extension: "a", Error: "some err a", Msg: "Ошибка А"},
-				&RequestResult{State: RequestOK, Extension: "b"},
-				&RequestResult{State: RequestError, Extension: "c", Error: "some err c", Msg: "Ошибка С"},
-				&RequestResult{State: RequestOK, Extension: "d"},
-			),
-			wrapErr: ErrInstall,
-			checkErr: func(t *testing.T, err error) {
-				assert.ErrorIs(t, err, ErrInstall)
-				errs := errors.GetErrors(err)
-				require.Len(t, errs, 2)
-
-				extErr1 := errs[0]
-				assert.Equal(t, "a", ExtensionFromError(extErr1))
-				assert.Equal(t, "some err a", extErr1.Error())
-				assert.Equal(t, "Ошибка А", errors.GetDetail(extErr1))
-
-				extErr2 := errs[1]
-				assert.Equal(t, "c", ExtensionFromError(extErr2))
-				assert.Equal(t, "some err c", extErr2.Error())
-				assert.Equal(t, "Ошибка С", errors.GetDetail(extErr2))
-			},
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			err := getErrors(tt.out, tt.err, tt.wrapErr)
-			if tt.checkErr != nil {
-				require.Error(t, err)
-				tt.checkErr(t, err)
-			} else {
-				require.NoError(t, err)
-			}
-		})
-	}
-}
diff --git a/pkg/extension/extension.go b/pkg/extension/extension.go
index 4af0a7734b6bb2368c2cbe3ffd2f6de008e3340b..771a945312dd8a6bdfb87b3ca6fa9970e8d28f0d 100644
--- a/pkg/extension/extension.go
+++ b/pkg/extension/extension.go
@@ -2,45 +2,31 @@ package extension
 
 import (
 	"context"
-	"fmt"
 
 	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	"git.perx.ru/perxis/perxis-go/pkg/content"
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
-	"git.perx.ru/perxis/perxis-go/pkg/items"
 	"git.perx.ru/perxis/perxis-go/pkg/schema"
 	"git.perx.ru/perxis/perxis-go/pkg/setup"
 	pb "git.perx.ru/perxis/perxis-go/proto/extensions"
 )
 
 const (
-	RequestOK    = pb.ExtensionRequestResult_OK
-	RequestError = pb.ExtensionRequestResult_ERROR
-
-	ExtensionsCollectionID   = "space_extensions"
-	ExtensionsCollectionName = "Настройки/Расширения"
-
-	StatePending    = pb.SpaceExtensions_PENDING
-	StateInstalled  = pb.SpaceExtensions_INSTALLED
-	StateInProgress = pb.SpaceExtensions_IN_PROGRESS
-	StateFail       = pb.SpaceExtensions_FAIL
+	StatePending    = pb.State_PENDING
+	StateInstalled  = pb.State_INSTALLED
+	StateInProgress = pb.State_IN_PROGRESS
+	StateFail       = pb.State_FAIL
 
 	MetadataKey = "extension"
 )
 
 type (
-	InstallRequest    = pb.InstallRequest
-	InstallResponse   = pb.InstallResponse
-	CheckRequest      = pb.CheckRequest
-	CheckResponse     = pb.CheckResponse
-	UpdateRequest     = pb.UpdateRequest
-	UpdateResponse    = pb.UpdateResponse
-	UninstallRequest  = pb.UninstallRequest
-	UninstallResponse = pb.UninstallResponse
-	RequestResult     = pb.ExtensionRequestResult
-
-	ExtensionDescriptor  = pb.ExtensionDescriptor
-	SpaceExtensionsState = pb.SpaceExtensions_State
+	InstallRequest   = pb.InstallRequest
+	CheckRequest     = pb.CheckRequest
+	UninstallRequest = pb.UninstallRequest
+
+	ExtensionDescriptor = pb.ExtensionDescriptor
+	State               = pb.State
 )
 
 var (
@@ -52,7 +38,6 @@ var (
 	ErrCheck     = errors.New("check failed")
 	ErrUninstall = errors.New("uninstall failed")
 
-	ErrUpdateAvailable  = errors.New("update available")
 	ErrNotInstalled     = errors.New("not installed")
 	ErrUnknownExtension = errors.New("unknown extension")
 )
@@ -74,9 +59,6 @@ type Extension interface {
 	// Check вызывается для проверки состояния расширения
 	Check(ctx context.Context, in *CheckRequest) error
 
-	// Update вызывается для обновления вресии расширения
-	Update(ctx context.Context, in *UpdateRequest) error
-
 	// Uninstall вызывается для удаления расширения из пространства
 	Uninstall(ctx context.Context, in *UninstallRequest) error
 
@@ -85,28 +67,11 @@ type Extension interface {
 }
 
 func CheckInstalled(ctx context.Context, content *content.Content, spaceID, envID, extension string) (bool, error) {
-	res, _, err := content.Items.Find(ctx, spaceID, envID, ExtensionsCollectionID,
-		&items.Filter{Q: []string{fmt.Sprintf("extension == '%s'", extension)}})
-
+	status, err := NewStorage(content).GetStatus(ctx, spaceID, envID, extension)
 	if err != nil {
 		return false, err
 	}
-
-	if len(res) == 0 || res[0].Data["extension_state"] != int64(StateInstalled) {
-		return false, nil
-	}
-
-	return true, nil
-}
-
-func ExtensionError(err error, ext string) error {
-	return errors.WithContext(err, "extension", ext)
-}
-
-func ExtensionFromError(err error) string {
-	v, _ := errors.ContextKey(err, "extension")
-	ext, _ := v.(string)
-	return ext
+	return status.Installed, nil
 }
 
 func isMetadataEqual(s1, s2 *schema.Schema) bool {
diff --git a/pkg/extension/manager.go b/pkg/extension/manager.go
index 958ac1788315fdaf4f8f4776099888a5af7b1b1d..d196bdad4a10c1069dee035cf9da3c9bd56ee534 100644
--- a/pkg/extension/manager.go
+++ b/pkg/extension/manager.go
@@ -15,6 +15,7 @@ type Manager interface {
 	RegisterExtensions(ctx context.Context, ext ...*ExtensionConnector) error
 	UnregisterExtensions(ctx context.Context, ext ...*ExtensionConnector) error
 	ListExtensions(ctx context.Context, filter *ListExtensionsFilter) ([]*ExtensionConnector, error)
+	GetInstalledExtensions(ctx context.Context, space, env string, extensions ...string) ([]*Status, error)
 }
 
 type (
diff --git a/pkg/extension/manager_client.go b/pkg/extension/manager_client.go
index 99057c3e75f22cb623d7aeff83ac0f9016d9383e..126cbe863ad5e4c1e9a1de407b16cc20dd4250e3 100644
--- a/pkg/extension/manager_client.go
+++ b/pkg/extension/manager_client.go
@@ -11,7 +11,7 @@ import (
 
 type ManagerClient struct {
 	*Client
-	manager pb.ExtensionManagerClient
+	manager pb.ExtensionManagerServiceClient
 	conn    *grpc.ClientConn
 }
 
@@ -29,7 +29,7 @@ func NewManagerClientWithAddr(addr string) (*ManagerClient, error) {
 
 func NewManagerClient(cc *grpc.ClientConn) *ManagerClient {
 	return &ManagerClient{
-		manager: pb.NewExtensionManagerClient(cc),
+		manager: pb.NewExtensionManagerServiceClient(cc),
 		conn:    cc,
 		Client:  NewClient(cc),
 	}
@@ -70,3 +70,15 @@ func (c *ManagerClient) ListExtensions(ctx context.Context, filter *ListExtensio
 
 	return exts, nil
 }
+
+func (c *ManagerClient) GetInstalledExtensions(ctx context.Context, space, env string, extensions ...string) ([]*Status, error) {
+	resp, err := c.manager.GetInstalledExtensions(ctx, &pb.GetInstalledExtensionsRequest{
+		Extensions: extensions,
+		SpaceId:    space,
+		EnvId:      env,
+	})
+	if err != nil {
+		return nil, err
+	}
+	return resp.Status, err
+}
diff --git a/pkg/extension/middleware/error_logging_middleware.go b/pkg/extension/middleware/error_logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..2b3ec2f688d680d5401ba145bd4a340cde5e9203
--- /dev/null
+++ b/pkg/extension/middleware/error_logging_middleware.go
@@ -0,0 +1,114 @@
+package middleware
+
+// Code generated by gowrap. DO NOT EDIT.
+// template: ../../../assets/templates/middleware/error_log
+// gowrap: http://github.com/hexdigest/gowrap
+
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/extension -i Manager -t ../../../assets/templates/middleware/error_log -o error_logging_middleware.go -l ""
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/pkg/extension"
+	"go.uber.org/zap"
+)
+
+// errorLoggingMiddleware implements extension.Manager that is instrumented with logging
+type errorLoggingMiddleware struct {
+	logger *zap.Logger
+	next   extension.Manager
+}
+
+// ErrorLoggingMiddleware instruments an implementation of the extension.Manager with simple logging
+func ErrorLoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next extension.Manager) extension.Manager {
+		return &errorLoggingMiddleware{
+			next:   next,
+			logger: logger,
+		}
+	}
+}
+
+func (m *errorLoggingMiddleware) Action(ctx context.Context, in *extension.ActionRequest) (ap1 *extension.ActionResponse, err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Action(ctx, in)
+}
+
+func (m *errorLoggingMiddleware) Check(ctx context.Context, in *extension.CheckRequest) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Check(ctx, in)
+}
+
+func (m *errorLoggingMiddleware) GetDescriptor() (ep1 *extension.ExtensionDescriptor) {
+	return m.next.GetDescriptor()
+}
+
+func (m *errorLoggingMiddleware) GetInstalledExtensions(ctx context.Context, space string, env string, extensions ...string) (spa1 []*extension.Status, err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.GetInstalledExtensions(ctx, space, env, extensions...)
+}
+
+func (m *errorLoggingMiddleware) Install(ctx context.Context, in *extension.InstallRequest) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Install(ctx, in)
+}
+
+func (m *errorLoggingMiddleware) ListExtensions(ctx context.Context, filter *extension.ListExtensionsFilter) (epa1 []*extension.ExtensionConnector, err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.ListExtensions(ctx, filter)
+}
+
+func (m *errorLoggingMiddleware) RegisterExtensions(ctx context.Context, ext ...*extension.ExtensionConnector) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.RegisterExtensions(ctx, ext...)
+}
+
+func (m *errorLoggingMiddleware) Uninstall(ctx context.Context, in *extension.UninstallRequest) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.Uninstall(ctx, in)
+}
+
+func (m *errorLoggingMiddleware) UnregisterExtensions(ctx context.Context, ext ...*extension.ExtensionConnector) (err error) {
+	logger := m.logger
+	defer func() {
+		if err != nil {
+			logger.Warn("response error", zap.Error(err))
+		}
+	}()
+	return m.next.UnregisterExtensions(ctx, ext...)
+}
diff --git a/pkg/extension/middleware/logging_middleware.go b/pkg/extension/middleware/logging_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..49eda62214b80cd0a614731333b194e1186fffbd
--- /dev/null
+++ b/pkg/extension/middleware/logging_middleware.go
@@ -0,0 +1,354 @@
+package middleware
+
+// Code generated by gowrap. DO NOT EDIT.
+// template: ../../../assets/templates/middleware/access_log
+// gowrap: http://github.com/hexdigest/gowrap
+
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/extension -i Manager -t ../../../assets/templates/middleware/access_log -o logging_middleware.go -l ""
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"git.perx.ru/perxis/perxis-go/pkg/auth"
+	"git.perx.ru/perxis/perxis-go/pkg/extension"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+)
+
+// loggingMiddleware implements extension.Manager that is instrumented with logging
+type loggingMiddleware struct {
+	logger *zap.Logger
+	next   extension.Manager
+}
+
+// LoggingMiddleware instruments an implementation of the extension.Manager with simple logging
+func LoggingMiddleware(logger *zap.Logger) Middleware {
+	return func(next extension.Manager) extension.Manager {
+		return &loggingMiddleware{
+			next:   next,
+			logger: logger,
+		}
+	}
+}
+
+func (m *loggingMiddleware) Action(ctx context.Context, in *extension.ActionRequest) (ap1 *extension.ActionResponse, err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx": ctx,
+		"in":  in} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Action.Request", fields...)
+
+	ap1, err = m.next.Action(ctx, in)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+	}
+
+	for k, v := range map[string]interface{}{
+		"ap1": ap1,
+		"err": err} {
+		if k == "err" {
+			err, _ := v.(error)
+			fields = append(fields, zap.Error(err))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Action.Response", fields...)
+
+	return ap1, err
+}
+
+func (m *loggingMiddleware) Check(ctx context.Context, in *extension.CheckRequest) (err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx": ctx,
+		"in":  in} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Check.Request", fields...)
+
+	err = m.next.Check(ctx, in)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+	}
+
+	for k, v := range map[string]interface{}{
+		"err": err} {
+		if k == "err" {
+			err, _ := v.(error)
+			fields = append(fields, zap.Error(err))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Check.Response", fields...)
+
+	return err
+}
+
+func (m *loggingMiddleware) GetDescriptor() (ep1 *extension.ExtensionDescriptor) {
+	begin := time.Now()
+	var fields []zapcore.Field
+
+	m.logger.Debug("GetDescriptor.Request", fields...)
+
+	ep1 = m.next.GetDescriptor()
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+	}
+
+	for k, v := range map[string]interface{}{
+		"ep1": ep1} {
+		if k == "err" {
+			err, _ := v.(error)
+			fields = append(fields, zap.Error(err))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("GetDescriptor.Response", fields...)
+
+	return ep1
+}
+
+func (m *loggingMiddleware) GetInstalledExtensions(ctx context.Context, space string, env string, extensions ...string) (spa1 []*extension.Status, err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx":        ctx,
+		"space":      space,
+		"env":        env,
+		"extensions": extensions} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("GetInstalledExtensions.Request", fields...)
+
+	spa1, err = m.next.GetInstalledExtensions(ctx, space, env, extensions...)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+	}
+
+	for k, v := range map[string]interface{}{
+		"spa1": spa1,
+		"err":  err} {
+		if k == "err" {
+			err, _ := v.(error)
+			fields = append(fields, zap.Error(err))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("GetInstalledExtensions.Response", fields...)
+
+	return spa1, err
+}
+
+func (m *loggingMiddleware) Install(ctx context.Context, in *extension.InstallRequest) (err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx": ctx,
+		"in":  in} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Install.Request", fields...)
+
+	err = m.next.Install(ctx, in)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+	}
+
+	for k, v := range map[string]interface{}{
+		"err": err} {
+		if k == "err" {
+			err, _ := v.(error)
+			fields = append(fields, zap.Error(err))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Install.Response", fields...)
+
+	return err
+}
+
+func (m *loggingMiddleware) ListExtensions(ctx context.Context, filter *extension.ListExtensionsFilter) (epa1 []*extension.ExtensionConnector, err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx":    ctx,
+		"filter": filter} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("ListExtensions.Request", fields...)
+
+	epa1, err = m.next.ListExtensions(ctx, filter)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+	}
+
+	for k, v := range map[string]interface{}{
+		"epa1": epa1,
+		"err":  err} {
+		if k == "err" {
+			err, _ := v.(error)
+			fields = append(fields, zap.Error(err))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("ListExtensions.Response", fields...)
+
+	return epa1, err
+}
+
+func (m *loggingMiddleware) RegisterExtensions(ctx context.Context, ext ...*extension.ExtensionConnector) (err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx": ctx,
+		"ext": ext} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("RegisterExtensions.Request", fields...)
+
+	err = m.next.RegisterExtensions(ctx, ext...)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+	}
+
+	for k, v := range map[string]interface{}{
+		"err": err} {
+		if k == "err" {
+			err, _ := v.(error)
+			fields = append(fields, zap.Error(err))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("RegisterExtensions.Response", fields...)
+
+	return err
+}
+
+func (m *loggingMiddleware) Uninstall(ctx context.Context, in *extension.UninstallRequest) (err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx": ctx,
+		"in":  in} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Uninstall.Request", fields...)
+
+	err = m.next.Uninstall(ctx, in)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+	}
+
+	for k, v := range map[string]interface{}{
+		"err": err} {
+		if k == "err" {
+			err, _ := v.(error)
+			fields = append(fields, zap.Error(err))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("Uninstall.Response", fields...)
+
+	return err
+}
+
+func (m *loggingMiddleware) UnregisterExtensions(ctx context.Context, ext ...*extension.ExtensionConnector) (err error) {
+	begin := time.Now()
+	var fields []zapcore.Field
+	for k, v := range map[string]interface{}{
+		"ctx": ctx,
+		"ext": ext} {
+		if k == "ctx" {
+			fields = append(fields, zap.String("principal", fmt.Sprint(auth.GetPrincipal(ctx))))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("UnregisterExtensions.Request", fields...)
+
+	err = m.next.UnregisterExtensions(ctx, ext...)
+
+	fields = []zapcore.Field{
+		zap.Duration("time", time.Since(begin)),
+	}
+
+	for k, v := range map[string]interface{}{
+		"err": err} {
+		if k == "err" {
+			err, _ := v.(error)
+			fields = append(fields, zap.Error(err))
+			continue
+		}
+		fields = append(fields, zap.Reflect(k, v))
+	}
+
+	m.logger.Debug("UnregisterExtensions.Response", fields...)
+
+	return err
+}
diff --git a/pkg/extension/middleware/middleware.go b/pkg/extension/middleware/middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..52b8bff724419d007ece694ade348182a738ce82
--- /dev/null
+++ b/pkg/extension/middleware/middleware.go
@@ -0,0 +1,28 @@
+package middleware
+
+// Code generated by gowrap. DO NOT EDIT.
+// template: ../../../assets/templates/middleware/middleware
+// gowrap: http://github.com/hexdigest/gowrap
+
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/extension -i Manager -t ../../../assets/templates/middleware/middleware -o middleware.go -l ""
+
+import (
+	"git.perx.ru/perxis/perxis-go/pkg/extension"
+	"go.uber.org/zap"
+)
+
+type Middleware func(extension.Manager) extension.Manager
+
+func WithLog(s extension.Manager, logger *zap.Logger, log_access bool) extension.Manager {
+	if logger == nil {
+		logger = zap.NewNop()
+	}
+
+	logger = logger.Named("Manager")
+	s = ErrorLoggingMiddleware(logger)(s)
+	if log_access {
+		s = LoggingMiddleware(logger)(s)
+	}
+	s = RecoveringMiddleware(logger)(s)
+	return s
+}
diff --git a/pkg/extension/middleware/recovering_middleware.go b/pkg/extension/middleware/recovering_middleware.go
new file mode 100644
index 0000000000000000000000000000000000000000..5d9a3b5681809fd3b46d8fe72dc66043c9246235
--- /dev/null
+++ b/pkg/extension/middleware/recovering_middleware.go
@@ -0,0 +1,138 @@
+package middleware
+
+// Code generated by gowrap. DO NOT EDIT.
+// template: ../../../assets/templates/middleware/recovery
+// gowrap: http://github.com/hexdigest/gowrap
+
+//go:generate gowrap gen -p git.perx.ru/perxis/perxis-go/pkg/extension -i Manager -t ../../../assets/templates/middleware/recovery -o recovering_middleware.go -l ""
+
+import (
+	"context"
+	"fmt"
+
+	"git.perx.ru/perxis/perxis-go/pkg/extension"
+	"go.uber.org/zap"
+)
+
+// recoveringMiddleware implements extension.Manager that is instrumented with logging
+type recoveringMiddleware struct {
+	logger *zap.Logger
+	next   extension.Manager
+}
+
+// RecoveringMiddleware instruments an implementation of the extension.Manager with simple logging
+func RecoveringMiddleware(logger *zap.Logger) Middleware {
+	return func(next extension.Manager) extension.Manager {
+		return &recoveringMiddleware{
+			next:   next,
+			logger: logger,
+		}
+	}
+}
+
+func (m *recoveringMiddleware) Action(ctx context.Context, in *extension.ActionRequest) (ap1 *extension.ActionResponse, err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.Action(ctx, in)
+}
+
+func (m *recoveringMiddleware) Check(ctx context.Context, in *extension.CheckRequest) (err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.Check(ctx, in)
+}
+
+func (m *recoveringMiddleware) GetDescriptor() (ep1 *extension.ExtensionDescriptor) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+		}
+	}()
+
+	return m.next.GetDescriptor()
+}
+
+func (m *recoveringMiddleware) GetInstalledExtensions(ctx context.Context, space string, env string, extensions ...string) (spa1 []*extension.Status, err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.GetInstalledExtensions(ctx, space, env, extensions...)
+}
+
+func (m *recoveringMiddleware) Install(ctx context.Context, in *extension.InstallRequest) (err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.Install(ctx, in)
+}
+
+func (m *recoveringMiddleware) ListExtensions(ctx context.Context, filter *extension.ListExtensionsFilter) (epa1 []*extension.ExtensionConnector, err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.ListExtensions(ctx, filter)
+}
+
+func (m *recoveringMiddleware) RegisterExtensions(ctx context.Context, ext ...*extension.ExtensionConnector) (err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.RegisterExtensions(ctx, ext...)
+}
+
+func (m *recoveringMiddleware) Uninstall(ctx context.Context, in *extension.UninstallRequest) (err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.Uninstall(ctx, in)
+}
+
+func (m *recoveringMiddleware) UnregisterExtensions(ctx context.Context, ext ...*extension.ExtensionConnector) (err error) {
+	logger := m.logger
+	defer func() {
+		if r := recover(); r != nil {
+			logger.Error("panic", zap.Error(fmt.Errorf("%v", r)))
+			err = fmt.Errorf("%v", r)
+		}
+	}()
+
+	return m.next.UnregisterExtensions(ctx, ext...)
+}
diff --git a/pkg/extension/mocks/Extension.go b/pkg/extension/mocks/Extension.go
index 87710ccff1d98aa5e0922d7e1563caad8c9f21f1..67270e599bad23a91aadfeda297325203fea5573 100644
--- a/pkg/extension/mocks/Extension.go
+++ b/pkg/extension/mocks/Extension.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.14.0. DO NOT EDIT.
+// Code generated by mockery v2.34.2. DO NOT EDIT.
 
 package mocks
 
@@ -20,6 +20,10 @@ func (_m *Extension) Action(ctx context.Context, in *extensions.ActionRequest) (
 	ret := _m.Called(ctx, in)
 
 	var r0 *extensions.ActionResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *extensions.ActionRequest) (*extensions.ActionResponse, error)); ok {
+		return rf(ctx, in)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *extensions.ActionRequest) *extensions.ActionResponse); ok {
 		r0 = rf(ctx, in)
 	} else {
@@ -28,7 +32,6 @@ func (_m *Extension) Action(ctx context.Context, in *extensions.ActionRequest) (
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *extensions.ActionRequest) error); ok {
 		r1 = rf(ctx, in)
 	} else {
@@ -96,27 +99,12 @@ func (_m *Extension) Uninstall(ctx context.Context, in *extensions.UninstallRequ
 	return r0
 }
 
-// Update provides a mock function with given fields: ctx, in
-func (_m *Extension) Update(ctx context.Context, in *extensions.UpdateRequest) error {
-	ret := _m.Called(ctx, in)
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, *extensions.UpdateRequest) error); ok {
-		r0 = rf(ctx, in)
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
-
-type mockConstructorTestingTNewExtension interface {
+// NewExtension creates a new instance of Extension. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewExtension(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewExtension creates a new instance of Extension. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewExtension(t mockConstructorTestingTNewExtension) *Extension {
+}) *Extension {
 	mock := &Extension{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/extension/mocks/Manager.go b/pkg/extension/mocks/Manager.go
index 9dd78407842bb31086066b1ebbee6fb2de180484..7d4266a30bc9012307f350bcfa9277d1b22e85f3 100644
--- a/pkg/extension/mocks/Manager.go
+++ b/pkg/extension/mocks/Manager.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.14.0. DO NOT EDIT.
+// Code generated by mockery v2.34.2. DO NOT EDIT.
 
 package mocks
 
@@ -21,6 +21,10 @@ func (_m *Manager) Action(ctx context.Context, in *extensions.ActionRequest) (*e
 	ret := _m.Called(ctx, in)
 
 	var r0 *extensions.ActionResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *extensions.ActionRequest) (*extensions.ActionResponse, error)); ok {
+		return rf(ctx, in)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *extensions.ActionRequest) *extensions.ActionResponse); ok {
 		r0 = rf(ctx, in)
 	} else {
@@ -29,7 +33,6 @@ func (_m *Manager) Action(ctx context.Context, in *extensions.ActionRequest) (*e
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *extensions.ActionRequest) error); ok {
 		r1 = rf(ctx, in)
 	} else {
@@ -69,6 +72,39 @@ func (_m *Manager) GetDescriptor() *extensions.ExtensionDescriptor {
 	return r0
 }
 
+// GetInstalledExtensions provides a mock function with given fields: ctx, space, env, _a3
+func (_m *Manager) GetInstalledExtensions(ctx context.Context, space string, env string, _a3 ...string) ([]*extensions.GetInstalledExtensionsResponse_Status, error) {
+	_va := make([]interface{}, len(_a3))
+	for _i := range _a3 {
+		_va[_i] = _a3[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, space, env)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 []*extensions.GetInstalledExtensionsResponse_Status
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, ...string) ([]*extensions.GetInstalledExtensionsResponse_Status, error)); ok {
+		return rf(ctx, space, env, _a3...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, ...string) []*extensions.GetInstalledExtensionsResponse_Status); ok {
+		r0 = rf(ctx, space, env, _a3...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*extensions.GetInstalledExtensionsResponse_Status)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, string, string, ...string) error); ok {
+		r1 = rf(ctx, space, env, _a3...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
 // Install provides a mock function with given fields: ctx, in
 func (_m *Manager) Install(ctx context.Context, in *extensions.InstallRequest) error {
 	ret := _m.Called(ctx, in)
@@ -88,6 +124,10 @@ func (_m *Manager) ListExtensions(ctx context.Context, filter *extensions.ListEx
 	ret := _m.Called(ctx, filter)
 
 	var r0 []*extension.ExtensionConnector
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *extensions.ListExtensionsFilter) ([]*extension.ExtensionConnector, error)); ok {
+		return rf(ctx, filter)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *extensions.ListExtensionsFilter) []*extension.ExtensionConnector); ok {
 		r0 = rf(ctx, filter)
 	} else {
@@ -96,7 +136,6 @@ func (_m *Manager) ListExtensions(ctx context.Context, filter *extensions.ListEx
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *extensions.ListExtensionsFilter) error); ok {
 		r1 = rf(ctx, filter)
 	} else {
@@ -162,27 +201,12 @@ func (_m *Manager) UnregisterExtensions(ctx context.Context, ext ...*extension.E
 	return r0
 }
 
-// Update provides a mock function with given fields: ctx, in
-func (_m *Manager) Update(ctx context.Context, in *extensions.UpdateRequest) error {
-	ret := _m.Called(ctx, in)
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, *extensions.UpdateRequest) error); ok {
-		r0 = rf(ctx, in)
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
-
-type mockConstructorTestingTNewManager interface {
+// NewManager creates a new instance of Manager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewManager(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewManager creates a new instance of Manager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewManager(t mockConstructorTestingTNewManager) *Manager {
+}) *Manager {
 	mock := &Manager{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/extension/mocks/Storage.go b/pkg/extension/mocks/Storage.go
new file mode 100644
index 0000000000000000000000000000000000000000..702f178fe0404c6957b652a4196066a69c1ee57e
--- /dev/null
+++ b/pkg/extension/mocks/Storage.go
@@ -0,0 +1,117 @@
+// Code generated by mockery v2.34.2. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	extensions "git.perx.ru/perxis/perxis-go/proto/extensions"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// Storage is an autogenerated mock type for the Storage type
+type Storage struct {
+	mock.Mock
+}
+
+// DeleteStatus provides a mock function with given fields: ctx, spaceID, envID, _a3
+func (_m *Storage) DeleteStatus(ctx context.Context, spaceID string, envID string, _a3 string) error {
+	ret := _m.Called(ctx, spaceID, envID, _a3)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, string) error); ok {
+		r0 = rf(ctx, spaceID, envID, _a3)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// FindStatuses provides a mock function with given fields: ctx, spaceID, envID, _a3
+func (_m *Storage) FindStatuses(ctx context.Context, spaceID string, envID string, _a3 ...string) ([]*extensions.GetInstalledExtensionsResponse_Status, error) {
+	_va := make([]interface{}, len(_a3))
+	for _i := range _a3 {
+		_va[_i] = _a3[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, spaceID, envID)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 []*extensions.GetInstalledExtensionsResponse_Status
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, ...string) ([]*extensions.GetInstalledExtensionsResponse_Status, error)); ok {
+		return rf(ctx, spaceID, envID, _a3...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, ...string) []*extensions.GetInstalledExtensionsResponse_Status); ok {
+		r0 = rf(ctx, spaceID, envID, _a3...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).([]*extensions.GetInstalledExtensionsResponse_Status)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, string, string, ...string) error); ok {
+		r1 = rf(ctx, spaceID, envID, _a3...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetStatus provides a mock function with given fields: ctx, spaceID, envID, _a3
+func (_m *Storage) GetStatus(ctx context.Context, spaceID string, envID string, _a3 string) (*extensions.GetInstalledExtensionsResponse_Status, error) {
+	ret := _m.Called(ctx, spaceID, envID, _a3)
+
+	var r0 *extensions.GetInstalledExtensionsResponse_Status
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (*extensions.GetInstalledExtensionsResponse_Status, error)); ok {
+		return rf(ctx, spaceID, envID, _a3)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, string) *extensions.GetInstalledExtensionsResponse_Status); ok {
+		r0 = rf(ctx, spaceID, envID, _a3)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*extensions.GetInstalledExtensionsResponse_Status)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok {
+		r1 = rf(ctx, spaceID, envID, _a3)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// SetStatus provides a mock function with given fields: ctx, spaceID, envID, _a3, status
+func (_m *Storage) SetStatus(ctx context.Context, spaceID string, envID string, _a3 string, status *extensions.GetInstalledExtensionsResponse_Status) error {
+	ret := _m.Called(ctx, spaceID, envID, _a3, status)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string, string, *extensions.GetInstalledExtensionsResponse_Status) error); ok {
+		r0 = rf(ctx, spaceID, envID, _a3, status)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewStorage(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *Storage {
+	mock := &Storage{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/pkg/extension/schema.go b/pkg/extension/schema.go
new file mode 100644
index 0000000000000000000000000000000000000000..cf71a4568c8ed734bda41e344936b728a8ed8b99
--- /dev/null
+++ b/pkg/extension/schema.go
@@ -0,0 +1,149 @@
+package extension
+
+import (
+	"sort"
+
+	"git.perx.ru/perxis/perxis-go/pkg/collections"
+	"git.perx.ru/perxis/perxis-go/pkg/references"
+	"git.perx.ru/perxis/perxis-go/pkg/schema"
+	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
+	"git.perx.ru/perxis/perxis-go/pkg/schema/validate"
+	pb "git.perx.ru/perxis/perxis-go/proto/extensions"
+)
+
+const (
+	StatusCollectionID   = "space_extensions"
+	StatusCollectionName = "Настройки/Расширения"
+
+	ActionsCollectionID   = "space_actions"
+	ActionsCollectionName = "Настройки/Действия"
+)
+
+// NewActionsCollection - создает коллекцию для хранения действий расширений в пространстве
+func NewActionsCollection(spaceID, envID string) *collections.Collection {
+	sch := schema.New(
+		"extension", field.String(validate.Required()).SetTitle("Расширение").SetTextSearch(true),
+		"action", field.String(validate.Required()).SetTitle("Действия").SetTextSearch(true),
+		"target", field.Number(
+			field.NumberFormatInt,
+			validate.Enum(getEnumOpt(pb.Target_name)...),
+		).SetTitle("Отображение результата действия").WithUI(&field.UI{Widget: "Select"}),
+		"parent", field.String().SetTitle("Идентификатор родительского действия"),
+		"name", field.String().SetTitle("Название").SetTextSearch(true),
+		"description", field.String().SetTitle("Описание"),
+		"icon", field.String().SetTitle("Название иконки"),
+		"image", references.Field([]string{"media"}).SetTitle("Изображение").
+			WithUI(&field.UI{Widget: "Media"}),
+		"groups", field.Array(field.String().SetAdditionalValues()).SetTitle("Группы").
+			SetDescription("Группировка действий в пользовательском интерфейсе").
+			WithUI(&field.UI{Widget: "SelectList"}),
+		"kind", field.Number(
+			field.NumberFormatInt,
+			validate.Enum(getEnumOpt(pb.Action_Kind_name)...),
+		).SetTitle("Р’РёРґ").WithUI(&field.UI{Widget: "Select"}),
+		"classes", field.Array(field.String().SetAdditionalValues()).SetTitle("Класс").
+			WithUI(&field.UI{Widget: "SelectList"}).
+			SetDescription("Классы данных, к которым применимо действие").
+			SetIndexed(true),
+		"refs", field.Array(references.Field(nil)).SetTitle("Ссылки").
+			WithUI(&field.UI{Widget: "BlockList"}),
+		"params_collection", field.String().SetTitle("Коллекция параметров").
+			SetDescription("ID коллекции для хранения параметров действия").WithUI(&field.UI{Widget: "Collection"}),
+		"navigation_action", field.Bool().SetTitle("Навигация").
+			SetDescription("Выполняется переход пользователя в пользовательском интерфейсе").
+			WithUI(&field.UI{Widget: "Checkbox"}),
+		"navigation_route", field.String().SetTitle("Путь в интерфейсе"),
+		"autorun", field.Bool().SetTitle("Автозапуск").
+			SetDescription("Действие выполняется автоматически каждый раз при загрузке приложения").
+			WithUI(&field.UI{Widget: "Checkbox"}),
+		"confirm", field.Bool().SetTitle("С подтверждением").
+			SetDescription("Пользователь должен подтвердить запуск действия").
+			WithUI(&field.UI{Widget: "Checkbox"}),
+		"view", field.Number(
+			field.NumberFormatInt,
+			validate.Enum(getEnumOpt(pb.Action_View_name)...),
+		).SetTitle("Отображение действия в интерфейсе").WithUI(&field.UI{Widget: "Select"}),
+		"order", field.Number(field.NumberFormatInt).SetTitle("Порядок отображения").
+			SetDescription("Порядок отображения действия в интерфейсе (Для пунктов меню)"),
+	)
+
+	// Includes
+	sch.SetIncludes(
+		field.Include{Ref: "hoop_item_options", Optional: true},
+	)
+
+	// UI
+	sch.Field.UI.ListView = &field.View{Options: map[string]interface{}{
+		"fields":    []string{"name", "action", "kind", "target", "updated_at", "updated_by"},
+		"sort":      []string{"name"},
+		"page_size": 50,
+	}}
+
+	sch.Field.UI.Options["title"] = "name"
+	sch.Field.UI.Options["subtitle"] = "action"
+	sch.Field.UI.Options["collection_icon"] = "SettingOutlined/RocketOutlined"
+
+	return &collections.Collection{
+		ID:      ActionsCollectionID,
+		SpaceID: spaceID,
+		EnvID:   envID,
+		Name:    ActionsCollectionName,
+		Schema:  sch,
+		Hidden:  true,
+	}
+}
+
+func getEnumOpt(opts map[int32]string) []validate.EnumOpt {
+	keys := make([]string, 0, len(opts))
+	for _, k := range opts {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+
+	enum := make([]validate.EnumOpt, len(opts))
+	for i, n := range keys {
+		enum[i] = validate.EnumOpt{Name: n, Value: float64(i)}
+	}
+	return enum
+}
+
+// NewStatusCollection - создает коллекцию для хранения статусов расширений в пространстве
+func NewStatusCollection(spaceID, envID string) *collections.Collection {
+	sch := schema.New(
+		"id", field.String(validate.Required()).SetUnique(true).SetTitle("Расширение"),
+		"title", field.String().SetTitle("Название"),
+		"version", field.String().SetTitle("Установленная версия"),
+		"status_error", field.String().SetTitle("Ошибка"),
+		"status_msg", field.String().SetTitle("Сообщение"),
+		"extension_state", field.Number(
+			field.NumberFormatInt,
+			validate.Enum(
+				validate.EnumOpt{Name: StateInstalled.String(), Value: float64(StateInstalled)},
+				validate.EnumOpt{Name: StateFail.String(), Value: float64(StateFail)},
+				validate.EnumOpt{Name: StatePending.String(), Value: float64(StatePending)},
+				validate.EnumOpt{Name: StateInProgress.String(), Value: float64(StateInProgress)},
+			),
+		).SetTitle("Состояние").WithUI(&field.UI{Widget: "Select"}),
+	)
+
+	//UI
+	sch.Field.UI.ListView = &field.View{Options: map[string]interface{}{
+		"fields":    []string{"id", "title", "updated_at", "updated_by"},
+		"sort":      []string{"title"},
+		"page_size": 50,
+	}}
+
+	sch.Field.UI.Options["title"] = "title"
+	sch.Field.UI.Options["subtitle"] = "id"
+	sch.Field.UI.Options["key"] = "id"
+	sch.Field.UI.Options["collection_icon"] = "SettingOutlined/ApiOutlined"
+
+	return &collections.Collection{
+		ID:      StatusCollectionID,
+		SpaceID: spaceID,
+		EnvID:   envID,
+		Name:    StatusCollectionName,
+		Schema:  sch,
+		Hidden:  true,
+	}
+}
diff --git a/pkg/extension/schema_test.go b/pkg/extension/schema_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b15a210045b85ef7349cbdaad3b7365eb6c411a0
--- /dev/null
+++ b/pkg/extension/schema_test.go
@@ -0,0 +1,34 @@
+package extension
+
+import (
+	"reflect"
+	"testing"
+
+	"git.perx.ru/perxis/perxis-go/pkg/schema/validate"
+)
+
+func Test_getEnumOpt(t *testing.T) {
+	tests := []struct {
+		name string
+		opts map[int32]string
+		want []validate.EnumOpt
+	}{
+		{
+			name: "#1",
+			opts: map[int32]string{1: "a", 2: "b", 3: "c", 1000: "w"},
+			want: []validate.EnumOpt{
+				{Name: "a", Value: float64(0)},
+				{Name: "b", Value: float64(1)},
+				{Name: "c", Value: float64(2)},
+				{Name: "w", Value: float64(3)},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := getEnumOpt(tt.opts); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("getEnumOpt() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/pkg/extension/server.go b/pkg/extension/server.go
index 467b4396c743d1d6bbdbcf3cf444199a6ecaf192..4e3c4fc2817869a32159cece4fb90706d3bbd36a 100644
--- a/pkg/extension/server.go
+++ b/pkg/extension/server.go
@@ -2,86 +2,164 @@ package extension
 
 import (
 	"context"
+	"strings"
 
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/pkg/operation"
+	commonpb "git.perx.ru/perxis/perxis-go/proto/common"
 	pb "git.perx.ru/perxis/perxis-go/proto/extensions"
+	"google.golang.org/grpc"
+	"google.golang.org/protobuf/proto"
 )
 
-type Server struct {
-	services map[string]Extension
-	pb.UnimplementedExtensionServer
+type RouteFn func(ctx context.Context, extensions ...string) ([]Extension, error)
+
+type WrapErrFn func(extension Extension, err error) error
+
+func DefaultWrapErrFn() WrapErrFn {
+	return func(_ Extension, err error) error { return err }
 }
 
-func NewServer(svc ...Extension) *Server {
-	srv := &Server{
-		services: make(map[string]Extension, len(svc)),
-	}
-	for _, s := range svc {
-		srv.services[s.GetDescriptor().Extension] = s
+type ServerOption func(c *Server)
+
+// WrapErrs Оборачивать ошибки, возвращаемые от каждого из расширений, в название расширения
+func WrapErrs() ServerOption {
+	return func(c *Server) {
+		c.wrapErr = func(extension Extension, err error) error {
+			return errors.Wrap(err, extension.GetDescriptor().Extension)
+		}
 	}
-	return srv
 }
 
-func getResult(ext string, err error) *RequestResult {
-	res := new(RequestResult)
-	res.Extension = ext
-	res.State = RequestOK
+// NamedExtensions Перенаправлять запросы к сервисам с названиями, соответствующими
+// списку расширений в XXXRequest. Если не найден сервис с названием, возвращается ошибка
+func NamedExtensions(extensions ...Extension) ServerOption {
+	return func(c *Server) {
+		m := make(map[string]Extension, len(extensions))
+		for _, e := range extensions {
+			m[e.GetDescriptor().Extension] = e
+		}
+		c.extensions = func(_ context.Context, extensions ...string) ([]Extension, error) {
+			var res []Extension
+			if len(extensions) == 0 {
+				for _, e := range m {
+					res = append(res, e)
+				}
+				return res, nil
+			}
 
-	if err != nil {
-		res.State = RequestError
-		res.Error = err.Error()
-		errs := errors.GetErrors(err)
-		if errs == nil {
-			errs = append(errs, err)
+			for _, ext := range extensions {
+				e, ok := m[ext]
+				if !ok {
+					return nil, errors.Wrap(ErrUnknownExtension, ext)
+				}
+				res = append(res, e)
+			}
+			return res, nil
 		}
-		for _, e := range errs {
-			res.Msg += errors.GetDetail(e) + "\n"
+		c.wrapErr = func(extension Extension, err error) error {
+			return errors.Wrap(err, extension.GetDescriptor().Extension)
 		}
 	}
-	return res
 }
 
-func (srv *Server) getResults(extensions []string, fn func(svc Extension) error) []*RequestResult {
+// SingleExtension Перенаправлять запросы к единому сервису
+func SingleExtension(extension Extension) ServerOption {
+	return func(c *Server) {
+		c.extensions = func(_ context.Context, _ ...string) ([]Extension, error) {
+			return []Extension{extension}, nil
+		}
+		c.wrapErr = DefaultWrapErrFn()
+	}
+}
 
-	var results []*RequestResult
+type Server struct {
+	extensions RouteFn
+	wrapErr    WrapErrFn
+	operations operation.Service
+	pb.UnimplementedExtensionServiceServer
+}
 
-	for _, e := range extensions {
-		svc, ok := srv.services[e]
-		if !ok {
-			results = append(results, getResult(e, ErrUnknownExtension))
-			continue
-		}
+func NewServer(options ...ServerOption) *Server {
+	srv := &Server{
+		operations: operation.NewDefaultService(),
+		wrapErr:    DefaultWrapErrFn(),
+	}
 
-		err := fn(svc)
-		results = append(results, getResult(e, err))
+	for _, o := range options {
+		o(srv)
 	}
 
-	return results
+	return srv
 }
 
-func (srv *Server) Install(ctx context.Context, request *InstallRequest) (*InstallResponse, error) {
-	res := srv.getResults(request.Extensions, func(svc Extension) error { return svc.Install(ctx, request) })
-	return &InstallResponse{Results: res}, nil
+func RegisterServer(grpcSrv *grpc.Server, srv *Server) {
+	commonpb.RegisterOperationServiceServer(grpcSrv, operation.NewServer(srv.operations))
+	pb.RegisterExtensionServiceServer(grpcSrv, srv)
 }
 
-func (srv *Server) Check(ctx context.Context, request *CheckRequest) (*CheckResponse, error) {
-	res := srv.getResults(request.Extensions, func(svc Extension) error { return svc.Check(ctx, request) })
-	return &CheckResponse{Results: res}, nil
+func (s *Server) Install(ctx context.Context, req *InstallRequest) (*operation.Proto, error) {
+	exts, err := s.extensions(ctx, req.Extensions...)
+	if err != nil {
+		return nil, err
+	}
+
+	desc := "Install extensions: " + strings.Join(req.Extensions, ", ")
+	op, err := s.operations.Create(ctx, desc,
+		func(ctx context.Context) (proto.Message, error) {
+			for _, ext := range exts {
+				if err := ext.Install(ctx, req); err != nil {
+					return nil, s.wrapErr(ext, err)
+				}
+			}
+			return nil, nil
+		})
+
+	return op.Proto(), err
 }
 
-func (srv *Server) Uninstall(ctx context.Context, request *UninstallRequest) (*UninstallResponse, error) {
-	res := srv.getResults(request.Extensions, func(svc Extension) error { return svc.Uninstall(ctx, request) })
-	return &UninstallResponse{Results: res}, nil
+func (s *Server) Uninstall(ctx context.Context, req *UninstallRequest) (*operation.Proto, error) {
+	exts, err := s.extensions(ctx, req.Extensions...)
+	if err != nil {
+		return nil, err
+	}
+
+	desc := "Uninstall extensions: " + strings.Join(req.Extensions, ", ")
+	op, err := s.operations.Create(ctx, desc,
+		func(ctx context.Context) (proto.Message, error) {
+			for _, ext := range exts {
+				if err := ext.Uninstall(ctx, req); err != nil {
+					return nil, s.wrapErr(ext, err)
+				}
+			}
+			return nil, nil
+		})
+
+	return op.Proto(), err
 }
 
-func (srv *Server) Update(ctx context.Context, request *UpdateRequest) (*UpdateResponse, error) {
-	res := srv.getResults(request.Extensions, func(svc Extension) error { return svc.Update(ctx, request) })
-	return &UpdateResponse{Results: res}, nil
+func (s *Server) Check(ctx context.Context, req *CheckRequest) (*operation.Proto, error) {
+	exts, err := s.extensions(ctx, req.Extensions...)
+	if err != nil {
+		return nil, err
+	}
+
+	desc := "Check extensions: " + strings.Join(req.Extensions, ", ")
+	op, err := s.operations.Create(ctx, desc,
+		func(ctx context.Context) (proto.Message, error) {
+			for _, ext := range exts {
+				if err := ext.Check(ctx, req); err != nil {
+					return nil, s.wrapErr(ext, err)
+				}
+			}
+			return nil, nil
+		})
+
+	return op.Proto(), err
 }
 
-func (srv *Server) getExtensionService(ctx context.Context, in *pb.ActionRequest) (Extension, error) {
+func (s *Server) Action(ctx context.Context, in *pb.ActionRequest) (*pb.ActionResponse, error) {
 	actionURL, err := NewActionURL(in.Action)
-
 	if err != nil {
 		return nil, ErrInvalidAction
 	}
@@ -94,21 +172,12 @@ func (srv *Server) getExtensionService(ctx context.Context, in *pb.ActionRequest
 		return nil, ErrInvalidAction
 	}
 
-	svc, ok := srv.services[ext]
-	if ok {
-		return svc, nil
-	}
-
-	return nil, ErrInvalidAction
-}
-
-func (srv *Server) Action(ctx context.Context, in *pb.ActionRequest) (out *pb.ActionResponse, err error) {
-	svc, err := srv.getExtensionService(ctx, in)
+	svc, err := s.extensions(ctx, ext)
 	if err != nil {
 		return nil, err
 	}
 
-	out, err = svc.Action(ctx, in)
+	out, err := svc[0].Action(ctx, in)
 
 	if out == nil {
 		out = &ActionResponse{}
@@ -123,9 +192,10 @@ func (srv *Server) Action(ctx context.Context, in *pb.ActionRequest) (out *pb.Ac
 	return out, nil
 }
 
-func (srv *Server) Start() error {
+func (s *Server) Start() error {
+	extensions, _ := s.extensions(context.Background())
 	var errs []error
-	for _, svc := range srv.services {
+	for _, svc := range extensions {
 		if r, ok := svc.(Runnable); ok {
 			if err := r.Start(); err != nil {
 				errs = append(errs, err)
@@ -140,9 +210,10 @@ func (srv *Server) Start() error {
 	return nil
 }
 
-func (srv *Server) Stop() error {
+func (s *Server) Stop() error {
+	extensions, _ := s.extensions(context.Background())
 	var errs []error
-	for _, svc := range srv.services {
+	for _, svc := range extensions {
 		if r, ok := svc.(Runnable); ok {
 			if err := r.Stop(); err != nil {
 				errs = append(errs, err)
diff --git a/pkg/extension/server_test.go b/pkg/extension/server_test.go
index 64f10e98705bc6e299e0c957d1deaa337e12f22d..5a0a1691cdb9c43d98f407d5a0a00ebb1563e446 100644
--- a/pkg/extension/server_test.go
+++ b/pkg/extension/server_test.go
@@ -3,86 +3,15 @@ package extension
 import (
 	"context"
 	"fmt"
-	"reflect"
 	"strings"
 	"testing"
 
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 )
 
-func TestGetResults(t *testing.T) {
-
-	getDummyExtension := func(name string, wantErr ...bool) Extension {
-		ext := &testServerExtension{name: name}
-
-		if len(wantErr) > 0 {
-			ext.err = errors.WithDetail(errors.New("some err"), "Ошибка")
-		}
-
-		return ext
-	}
-
-	tests := []struct {
-		name       string
-		services   []Extension
-		extensions []string
-		fn         func(svc Extension) error
-		want       []*RequestResult
-	}{
-		{
-			name:       "one extension without errors",
-			services:   []Extension{getDummyExtension("a"), getDummyExtension("b")},
-			extensions: []string{"a"},
-			fn:         func(svc Extension) error { return nil },
-			want: []*RequestResult{
-				{Extension: "a", State: RequestOK},
-			},
-		},
-		{
-			name:       "multiple extensions without errors",
-			services:   []Extension{getDummyExtension("a"), getDummyExtension("b"), getDummyExtension("c")},
-			extensions: []string{"a", "c"},
-			fn:         func(svc Extension) error { return nil },
-			want: []*RequestResult{
-				{Extension: "a", State: RequestOK},
-				{Extension: "c", State: RequestOK},
-			},
-		},
-		{
-			name:       "multiple extensions, one returns error",
-			services:   []Extension{getDummyExtension("a"), getDummyExtension("b"), getDummyExtension("c", true)},
-			extensions: []string{"a", "c"},
-			fn:         func(svc Extension) error { return svc.Install(nil, nil) },
-			want: []*RequestResult{
-				{Extension: "a", State: RequestOK},
-				{Extension: "c", State: RequestError, Error: "some err", Msg: "Ошибка\n"},
-			},
-		},
-		{
-			name:       "multiple extensions, all return error",
-			services:   []Extension{getDummyExtension("a", true), getDummyExtension("b", true), getDummyExtension("c", true)},
-			extensions: []string{"a", "b", "c"},
-			fn:         func(svc Extension) error { return svc.Install(nil, nil) },
-			want: []*RequestResult{
-				{Extension: "a", State: RequestError, Error: "some err", Msg: "Ошибка\n"},
-				{Extension: "b", State: RequestError, Error: "some err", Msg: "Ошибка\n"},
-				{Extension: "c", State: RequestError, Error: "some err", Msg: "Ошибка\n"},
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			srv := NewServer(tt.services...)
-			if got := srv.getResults(tt.extensions, tt.fn); !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("getResults() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
-
-// не подходит использование mock.Extension из-за возникающих циклических импортов
+// Использование mock.Extension не подходит из-за возникающих циклических импортов
 type testServerExtension struct {
 	err  error
 	name string
@@ -97,35 +26,33 @@ func (t testServerExtension) GetDescriptor() *ExtensionDescriptor {
 	}
 }
 
-func (t testServerExtension) Install(ctx context.Context, in *InstallRequest) error     { return t.err }
-func (t testServerExtension) Check(ctx context.Context, in *CheckRequest) error         { return t.err }
-func (t testServerExtension) Update(ctx context.Context, in *UpdateRequest) error       { return t.err }
-func (t testServerExtension) Uninstall(ctx context.Context, in *UninstallRequest) error { return t.err }
-func (t testServerExtension) Action(ctx context.Context, in *ActionRequest) (*ActionResponse, error) {
+func (t testServerExtension) Install(_ context.Context, _ *InstallRequest) error     { return t.err }
+func (t testServerExtension) Check(_ context.Context, _ *CheckRequest) error         { return t.err }
+func (t testServerExtension) Uninstall(_ context.Context, _ *UninstallRequest) error { return t.err }
+func (t testServerExtension) Action(_ context.Context, _ *ActionRequest) (*ActionResponse, error) {
 	return &ActionResponse{}, t.err
 }
 
-func TestServer_Action(t *testing.T) {
-	getDummyExtension := func(name string, wantErr ...bool) Extension {
-		ext := &testServerExtension{name: name}
-
-		if len(wantErr) > 0 {
-			ext.err = errors.WithDetail(errors.New("some err"), "Ошибка")
-		}
-
-		return ext
+func getDummyExtension(name string, wantErr ...string) Extension {
+	ext := &testServerExtension{name: name}
+	if len(wantErr) > 0 {
+		ext.err = errors.WithDetail(errors.New(wantErr[0]), "Ошибка")
 	}
+	return ext
+}
+
+func TestServer_Action(t *testing.T) {
 
 	var tests = []struct {
-		name     string
-		services map[string]Extension
-		in       *ActionRequest
-		want     *ActionResponse
-		wantErr  string
+		name      string
+		extension Extension
+		in        *ActionRequest
+		want      *ActionResponse
+		wantErr   string
 	}{
 		{
-			name:     "GRPC",
-			services: map[string]Extension{"test-extension": getDummyExtension("test-extension")},
+			name:      "GRPC",
+			extension: getDummyExtension("test-extension"),
 			in: &ActionRequest{
 				Action:  "grpc:///test-extension/test-action",
 				SpaceId: "sp",
@@ -134,8 +61,8 @@ func TestServer_Action(t *testing.T) {
 			want: &ActionResponse{State: ResponseDone},
 		},
 		{
-			name:     "invalid schema",
-			services: map[string]Extension{"test-extension": getDummyExtension("test-extension")},
+			name:      "invalid schema",
+			extension: getDummyExtension("test-extension"),
 			in: &ActionRequest{
 				Action:  "some:///space/env/coll",
 				SpaceId: "sp",
@@ -145,8 +72,8 @@ func TestServer_Action(t *testing.T) {
 			wantErr: "invalid action",
 		},
 		{
-			name:     "Deprecated call",
-			services: map[string]Extension{"test-extension": getDummyExtension("test-extension")},
+			name:      "Deprecated call",
+			extension: getDummyExtension("test-extension"),
 			in: &ActionRequest{
 				Action:    "test-action",
 				SpaceId:   "sp",
@@ -156,19 +83,19 @@ func TestServer_Action(t *testing.T) {
 			want: &ActionResponse{State: ResponseDone},
 		},
 		{
-			name:     "unknown extension",
-			services: map[string]Extension{"test-extension": getDummyExtension("test-extension")},
+			name:      "unknown extension",
+			extension: getDummyExtension("test-extension"),
 			in: &ActionRequest{
 				Action:  "grpc:///test-extension-2/test-action",
 				SpaceId: "sp",
 				EnvId:   "env",
 			},
 			want:    nil,
-			wantErr: "invalid action",
+			wantErr: errors.Wrap(ErrUnknownExtension, "test-extension-2").Error(),
 		},
 		{
-			name:     "Deprecated call, without extension",
-			services: map[string]Extension{"test-extension": getDummyExtension("test-extension")},
+			name:      "Deprecated call, without extension",
+			extension: getDummyExtension("test-extension"),
 			in: &ActionRequest{
 				Action:  "test-action",
 				SpaceId: "sp",
@@ -178,8 +105,8 @@ func TestServer_Action(t *testing.T) {
 			wantErr: "invalid action",
 		},
 		{
-			name:     "Deprecated call, without action and extension)",
-			services: map[string]Extension{"test-extension": getDummyExtension("test-extension")},
+			name:      "Deprecated call, without action and extension)",
+			extension: getDummyExtension("test-extension"),
 			in: &ActionRequest{
 				SpaceId: "sp",
 				EnvId:   "env",
@@ -191,14 +118,14 @@ func TestServer_Action(t *testing.T) {
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 
-			srv := &Server{
-				services: tt.services,
-			}
+			srv := NewServer(NamedExtensions(tt.extension))
 			got, err := srv.Action(context.Background(), tt.in)
 			if tt.wantErr != "" {
+				require.Error(t, err)
 				assert.EqualErrorf(t, err, tt.wantErr, fmt.Sprintf("Action(%v)", tt.in))
 				return
 			}
+			require.NoError(t, err)
 			assert.Equalf(t, tt.want, got, "Action(%v)", tt.in)
 		})
 	}
diff --git a/pkg/extension/service/extension.go b/pkg/extension/service/extension.go
index 951a00bd487bc5c14253e8d7ca4c1c49b78f0120..995c343467fef3a9b1963ef5d65a1c230ad4c69d 100644
--- a/pkg/extension/service/extension.go
+++ b/pkg/extension/service/extension.go
@@ -147,10 +147,6 @@ func (s *Extension) Check(ctx context.Context, in *extension.CheckRequest) error
 	return s.GetSetup(in.SpaceId, in.EnvId).Check(ctx)
 }
 
-func (s *Extension) Update(ctx context.Context, in *extension.UpdateRequest) error {
-	return s.GetSetup(in.SpaceId, in.EnvId).WithForce(in.Force).Install(ctx)
-}
-
 func (s *Extension) Uninstall(ctx context.Context, in *extension.UninstallRequest) error {
 	return s.GetSetup(in.SpaceId, in.EnvId).WithForce(in.Force).WithRemove(in.Remove).Uninstall(ctx)
 }
diff --git a/pkg/extension/storage.go b/pkg/extension/storage.go
new file mode 100644
index 0000000000000000000000000000000000000000..35a8f559bd9ba3042ec5c6569b1f821656a53afd
--- /dev/null
+++ b/pkg/extension/storage.go
@@ -0,0 +1,149 @@
+package extension
+
+import (
+	"context"
+	"strings"
+
+	"git.perx.ru/perxis/perxis-go/pkg/collections"
+	"git.perx.ru/perxis/perxis-go/pkg/data"
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"git.perx.ru/perxis/perxis-go/pkg/setup"
+	pb "git.perx.ru/perxis/perxis-go/proto/extensions"
+
+	"git.perx.ru/perxis/perxis-go/pkg/content"
+	"git.perx.ru/perxis/perxis-go/pkg/items"
+)
+
+type Status = pb.GetInstalledExtensionsResponse_Status
+
+// Storage описывает интерфейс хранилища состояний расширений
+type Storage interface {
+	GetStatus(ctx context.Context, spaceID, envID string, extension string) (*Status, error)
+	FindStatuses(ctx context.Context, spaceID, envID string, extensions ...string) ([]*Status, error)
+	SetStatus(ctx context.Context, spaceID, envID string, extension string, status *Status) error
+	DeleteStatus(ctx context.Context, spaceID, envID string, extension string) error
+}
+
+type storage struct {
+	content *content.Content
+}
+
+func NewStorage(content *content.Content) Storage {
+	return &storage{content: content}
+}
+
+func statusFromItem(extension string, item *items.Item) *Status {
+	if item == nil {
+		return &Status{
+			Extension: extension,
+			Installed: false,
+		}
+	}
+
+	d := item.Data
+
+	title, _ := d["title"].(string)
+	state, _ := d["extension_state"].(int64)
+	ver, _ := d["version"].(string)
+	msg, _ := d["status_msg"].(string)
+	errmsg, _ := d["status_error"].(string)
+
+	return &Status{
+		Extension:        extension,
+		Title:            title,
+		State:            State(state),
+		Msg:              msg,
+		Error:            errmsg,
+		Installed:        State(state) == StateInstalled,
+		InstalledVersion: ver,
+	}
+}
+
+// GetStatus возвращает состояние расширения в пространстве
+func (s *storage) GetStatus(ctx context.Context, spaceID, envID string, extension string) (*Status, error) {
+	res, err := s.content.Items.Get(ctx, spaceID, envID, StatusCollectionID, extension)
+	if err != nil && !strings.Contains(err.Error(), "not found") {
+		return nil, err
+	}
+	return statusFromItem(extension, res), nil
+}
+
+// FindStatuses возвращает состояния расширений в пространстве. Если расширение не установлено,
+// возвращается статус с флагом `Installed: false`. Статусы возвращаются в том же порядке, что и
+// переданные расширения
+func (s *storage) FindStatuses(ctx context.Context, spaceID, envID string, extensions ...string) ([]*Status, error) {
+	itms, _, err := s.content.Items.Find(ctx, spaceID, envID, StatusCollectionID, &items.Filter{ID: extensions})
+	if err != nil && !strings.Contains(err.Error(), collections.ErrNotFound.Error()) {
+		return nil, err
+	}
+
+	res := make([]*Status, 0, len(itms))
+
+	if len(extensions) == 0 {
+		for _, item := range itms {
+			res = append(res, statusFromItem(item.ID, item))
+		}
+		return res, nil
+	}
+
+	extensionToItem := make(map[string]*items.Item, len(itms))
+	for _, item := range itms {
+		extensionToItem[item.ID] = item
+	}
+	for _, extension := range extensions {
+		res = append(res, statusFromItem(extension, extensionToItem[extension]))
+	}
+
+	return res, nil
+}
+
+// SetStatus устанавливает состояние расширения в пространстве
+func (s *storage) SetStatus(ctx context.Context, spaceID, envID string, extension string, status *Status) error {
+	if err := s.init(ctx, spaceID, envID); err != nil {
+		return errors.Wrap(err, "prepare collections")
+	}
+
+	item := &items.Item{
+		ID:           extension,
+		SpaceID:      spaceID,
+		EnvID:        envID,
+		CollectionID: StatusCollectionID,
+		Data:         make(map[string]interface{}),
+	}
+
+	_ = data.Set("id", item.Data, extension) // item.Set устанавливает идентификатор записи
+	_ = item.Set("title", status.Title)
+	_ = item.Set("extension_state", float64(status.State))
+	_ = item.Set("version", status.InstalledVersion)
+	_ = item.Set("status_msg", status.Msg)
+	_ = item.Set("status_error", status.Error)
+
+	i, _ := s.content.Items.Get(ctx, spaceID, envID, StatusCollectionID, extension)
+	if i == nil {
+		_, err := s.content.Items.Create(ctx, item)
+		return err
+	}
+
+	return s.content.Items.Update(ctx, item)
+}
+
+// DeleteStatus удаляет состояние расширения в пространстве (при удалении расширения)
+func (s *storage) DeleteStatus(ctx context.Context, spaceID, envID string, extension string) error {
+	item := &items.Item{
+		ID:           extension,
+		SpaceID:      spaceID,
+		EnvID:        envID,
+		CollectionID: StatusCollectionID,
+	}
+	return s.content.Items.Delete(ctx, item)
+}
+
+func (s *storage) init(ctx context.Context, spaceID, envID string) error {
+	// миграция окружения не должна запуститься, поскольку окружение может быть сломано
+	// расширениями - нужно дать возможность восстановиться
+	stp := setup.NewSetup(s.content, spaceID, envID, nil).AddCollections([]*collections.Collection{
+		NewStatusCollection(spaceID, envID),
+		NewActionsCollection(spaceID, envID),
+	}, setup.SkipMigration())
+	return stp.Install(ctx)
+}
diff --git a/pkg/files/mocks/Downloader.go b/pkg/files/mocks/Downloader.go
index 99c3e32cb685b311c4e66827ce6f9eef615f73d4..099ac390d135d7d4bb92debd3f7aacf26ca1cbff 100644
--- a/pkg/files/mocks/Downloader.go
+++ b/pkg/files/mocks/Downloader.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -29,13 +29,12 @@ func (_m *Downloader) Download(dst io.Writer, file *files.File) error {
 	return r0
 }
 
-type mockConstructorTestingTNewDownloader interface {
+// NewDownloader creates a new instance of Downloader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewDownloader(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewDownloader creates a new instance of Downloader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewDownloader(t mockConstructorTestingTNewDownloader) *Downloader {
+}) *Downloader {
 	mock := &Downloader{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/files/mocks/Files.go b/pkg/files/mocks/Files.go
index fa78a1809f968dbc21a10e4154284c07261a90ce..16e213ab3a0cbac0fb1f348bff9a0d5a45203bab 100644
--- a/pkg/files/mocks/Files.go
+++ b/pkg/files/mocks/Files.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -33,6 +33,10 @@ func (_m *Files) CompleteUpload(ctx context.Context, upload *files.MultipartUplo
 	ret := _m.Called(ctx, upload)
 
 	var r0 *files.MultipartUpload
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.MultipartUpload) (*files.MultipartUpload, error)); ok {
+		return rf(ctx, upload)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *files.MultipartUpload) *files.MultipartUpload); ok {
 		r0 = rf(ctx, upload)
 	} else {
@@ -41,7 +45,6 @@ func (_m *Files) CompleteUpload(ctx context.Context, upload *files.MultipartUplo
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *files.MultipartUpload) error); ok {
 		r1 = rf(ctx, upload)
 	} else {
@@ -70,6 +73,10 @@ func (_m *Files) GetFile(ctx context.Context, file *files.File) (*files.File, er
 	ret := _m.Called(ctx, file)
 
 	var r0 *files.File
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.File) (*files.File, error)); ok {
+		return rf(ctx, file)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *files.File) *files.File); ok {
 		r0 = rf(ctx, file)
 	} else {
@@ -78,7 +85,6 @@ func (_m *Files) GetFile(ctx context.Context, file *files.File) (*files.File, er
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *files.File) error); ok {
 		r1 = rf(ctx, file)
 	} else {
@@ -93,6 +99,10 @@ func (_m *Files) MoveUpload(ctx context.Context, upload *files.MultipartUpload)
 	ret := _m.Called(ctx, upload)
 
 	var r0 *files.File
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.MultipartUpload) (*files.File, error)); ok {
+		return rf(ctx, upload)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *files.MultipartUpload) *files.File); ok {
 		r0 = rf(ctx, upload)
 	} else {
@@ -101,7 +111,6 @@ func (_m *Files) MoveUpload(ctx context.Context, upload *files.MultipartUpload)
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *files.MultipartUpload) error); ok {
 		r1 = rf(ctx, upload)
 	} else {
@@ -116,6 +125,10 @@ func (_m *Files) StartUpload(ctx context.Context, upload *files.MultipartUpload)
 	ret := _m.Called(ctx, upload)
 
 	var r0 *files.MultipartUpload
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.MultipartUpload) (*files.MultipartUpload, error)); ok {
+		return rf(ctx, upload)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *files.MultipartUpload) *files.MultipartUpload); ok {
 		r0 = rf(ctx, upload)
 	} else {
@@ -124,7 +137,6 @@ func (_m *Files) StartUpload(ctx context.Context, upload *files.MultipartUpload)
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *files.MultipartUpload) error); ok {
 		r1 = rf(ctx, upload)
 	} else {
@@ -139,6 +151,10 @@ func (_m *Files) Upload(ctx context.Context, file *files.File) (*files.Upload, e
 	ret := _m.Called(ctx, file)
 
 	var r0 *files.Upload
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.File) (*files.Upload, error)); ok {
+		return rf(ctx, file)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *files.File) *files.Upload); ok {
 		r0 = rf(ctx, file)
 	} else {
@@ -147,7 +163,6 @@ func (_m *Files) Upload(ctx context.Context, file *files.File) (*files.Upload, e
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *files.File) error); ok {
 		r1 = rf(ctx, file)
 	} else {
@@ -157,13 +172,12 @@ func (_m *Files) Upload(ctx context.Context, file *files.File) (*files.Upload, e
 	return r0, r1
 }
 
-type mockConstructorTestingTNewFiles interface {
+// NewFiles creates a new instance of Files. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewFiles(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewFiles creates a new instance of Files. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewFiles(t mockConstructorTestingTNewFiles) *Files {
+}) *Files {
 	mock := &Files{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/files/mocks/Middleware.go b/pkg/files/mocks/Middleware.go
index 4a47061df1e1fe7b868cba7da09a5615cf313985..f857bcd35fd5f610dda001de0f47dcde038935c8 100644
--- a/pkg/files/mocks/Middleware.go
+++ b/pkg/files/mocks/Middleware.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -29,13 +29,12 @@ func (_m *Middleware) Execute(_a0 files.Files) files.Files {
 	return r0
 }
 
-type mockConstructorTestingTNewMiddleware interface {
+// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMiddleware(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewMiddleware(t mockConstructorTestingTNewMiddleware) *Middleware {
+}) *Middleware {
 	mock := &Middleware{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/files/mocks/Storage.go b/pkg/files/mocks/Storage.go
index 7d4a411253dc517fb1a034f79035cf85756daccd..64e7974f1532be0cbba77c7cd23f8ecd4fa56918 100644
--- a/pkg/files/mocks/Storage.go
+++ b/pkg/files/mocks/Storage.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -33,6 +33,10 @@ func (_m *Storage) CompleteUpload(ctx context.Context, upload *files.MultipartUp
 	ret := _m.Called(ctx, upload)
 
 	var r0 *files.MultipartUpload
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.MultipartUpload) (*files.MultipartUpload, error)); ok {
+		return rf(ctx, upload)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *files.MultipartUpload) *files.MultipartUpload); ok {
 		r0 = rf(ctx, upload)
 	} else {
@@ -41,7 +45,6 @@ func (_m *Storage) CompleteUpload(ctx context.Context, upload *files.MultipartUp
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *files.MultipartUpload) error); ok {
 		r1 = rf(ctx, upload)
 	} else {
@@ -70,6 +73,10 @@ func (_m *Storage) GetFile(ctx context.Context, file *files.File) (*files.File,
 	ret := _m.Called(ctx, file)
 
 	var r0 *files.File
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.File) (*files.File, error)); ok {
+		return rf(ctx, file)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *files.File) *files.File); ok {
 		r0 = rf(ctx, file)
 	} else {
@@ -78,7 +85,6 @@ func (_m *Storage) GetFile(ctx context.Context, file *files.File) (*files.File,
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *files.File) error); ok {
 		r1 = rf(ctx, file)
 	} else {
@@ -93,6 +99,10 @@ func (_m *Storage) Move(ctx context.Context, src *files.File, dst *files.File) (
 	ret := _m.Called(ctx, src, dst)
 
 	var r0 *files.File
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.File, *files.File) (*files.File, error)); ok {
+		return rf(ctx, src, dst)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *files.File, *files.File) *files.File); ok {
 		r0 = rf(ctx, src, dst)
 	} else {
@@ -101,7 +111,6 @@ func (_m *Storage) Move(ctx context.Context, src *files.File, dst *files.File) (
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *files.File, *files.File) error); ok {
 		r1 = rf(ctx, src, dst)
 	} else {
@@ -116,6 +125,10 @@ func (_m *Storage) StartUpload(ctx context.Context, upload *files.MultipartUploa
 	ret := _m.Called(ctx, upload)
 
 	var r0 *files.MultipartUpload
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.MultipartUpload) (*files.MultipartUpload, error)); ok {
+		return rf(ctx, upload)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *files.MultipartUpload) *files.MultipartUpload); ok {
 		r0 = rf(ctx, upload)
 	} else {
@@ -124,7 +137,6 @@ func (_m *Storage) StartUpload(ctx context.Context, upload *files.MultipartUploa
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *files.MultipartUpload) error); ok {
 		r1 = rf(ctx, upload)
 	} else {
@@ -139,6 +151,10 @@ func (_m *Storage) Upload(ctx context.Context, file *files.File) (*files.Upload,
 	ret := _m.Called(ctx, file)
 
 	var r0 *files.Upload
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.File) (*files.Upload, error)); ok {
+		return rf(ctx, file)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *files.File) *files.Upload); ok {
 		r0 = rf(ctx, file)
 	} else {
@@ -147,7 +163,6 @@ func (_m *Storage) Upload(ctx context.Context, file *files.File) (*files.Upload,
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *files.File) error); ok {
 		r1 = rf(ctx, file)
 	} else {
@@ -157,13 +172,12 @@ func (_m *Storage) Upload(ctx context.Context, file *files.File) (*files.Upload,
 	return r0, r1
 }
 
-type mockConstructorTestingTNewStorage interface {
+// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewStorage(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewStorage(t mockConstructorTestingTNewStorage) *Storage {
+}) *Storage {
 	mock := &Storage{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/files/mocks/Uploader.go b/pkg/files/mocks/Uploader.go
index 4ae80e348de2001158ff9e93c9a65180a06c9165..5e6e9f1414e437e2cf078cf2f576b73d0b0b7cd5 100644
--- a/pkg/files/mocks/Uploader.go
+++ b/pkg/files/mocks/Uploader.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -29,13 +29,12 @@ func (_m *Uploader) Upload(src io.Reader, upload *files.Upload) error {
 	return r0
 }
 
-type mockConstructorTestingTNewUploader interface {
+// NewUploader creates a new instance of Uploader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUploader(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewUploader creates a new instance of Uploader. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewUploader(t mockConstructorTestingTNewUploader) *Uploader {
+}) *Uploader {
 	mock := &Uploader{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/items/mocks/Decoder.go b/pkg/items/mocks/Decoder.go
new file mode 100644
index 0000000000000000000000000000000000000000..b1dbd9aa526cf4fe4a62d29f652c3e2a513924b3
--- /dev/null
+++ b/pkg/items/mocks/Decoder.go
@@ -0,0 +1,41 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	items "git.perx.ru/perxis/perxis-go/pkg/items"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// Decoder is an autogenerated mock type for the Decoder type
+type Decoder struct {
+	mock.Mock
+}
+
+// Decode provides a mock function with given fields: value, item
+func (_m *Decoder) Decode(value interface{}, item *items.Item) error {
+	ret := _m.Called(value, item)
+
+	var r0 error
+	if rf, ok := ret.Get(0).(func(interface{}, *items.Item) error); ok {
+		r0 = rf(value, item)
+	} else {
+		r0 = ret.Error(0)
+	}
+
+	return r0
+}
+
+// NewDecoder creates a new instance of Decoder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewDecoder(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *Decoder {
+	mock := &Decoder{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/pkg/items/mocks/Encoder.go b/pkg/items/mocks/Encoder.go
new file mode 100644
index 0000000000000000000000000000000000000000..c19203b2448930eb75e62195837a1b22cd9cfe42
--- /dev/null
+++ b/pkg/items/mocks/Encoder.go
@@ -0,0 +1,53 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	items "git.perx.ru/perxis/perxis-go/pkg/items"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// Encoder is an autogenerated mock type for the Encoder type
+type Encoder struct {
+	mock.Mock
+}
+
+// Encode provides a mock function with given fields: item
+func (_m *Encoder) Encode(item *items.Item) (interface{}, error) {
+	ret := _m.Called(item)
+
+	var r0 interface{}
+	var r1 error
+	if rf, ok := ret.Get(0).(func(*items.Item) (interface{}, error)); ok {
+		return rf(item)
+	}
+	if rf, ok := ret.Get(0).(func(*items.Item) interface{}); ok {
+		r0 = rf(item)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(interface{})
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(*items.Item) error); ok {
+		r1 = rf(item)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewEncoder creates a new instance of Encoder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewEncoder(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *Encoder {
+	mock := &Encoder{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/pkg/items/mocks/ItemObserver.go b/pkg/items/mocks/ItemObserver.go
index c4b4954c210235ff09677615cdffdcb2b2fa38b7..d826e04045f01e504200e742afa16e68181bc6c9 100644
--- a/pkg/items/mocks/ItemObserver.go
+++ b/pkg/items/mocks/ItemObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.27.1. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -9,13 +9,12 @@ type ItemObserver struct {
 	mock.Mock
 }
 
-type mockConstructorTestingTNewItemObserver interface {
+// NewItemObserver creates a new instance of ItemObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewItemObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewItemObserver creates a new instance of ItemObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewItemObserver(t mockConstructorTestingTNewItemObserver) *ItemObserver {
+}) *ItemObserver {
 	mock := &ItemObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/items/mocks/ItemReadObserver.go b/pkg/items/mocks/ItemReadObserver.go
index cdbc43d4ce043adce90daabeda427b8bd9f3d670..212f09ad7a046086f1cc99230699a304ec2a9878 100644
--- a/pkg/items/mocks/ItemReadObserver.go
+++ b/pkg/items/mocks/ItemReadObserver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.27.1. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -101,13 +101,12 @@ func (_m *ItemReadObserver) OnPreGet(ctx context.Context, spaceId string, envId
 	return r0
 }
 
-type mockConstructorTestingTNewItemReadObserver interface {
+// NewItemReadObserver creates a new instance of ItemReadObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewItemReadObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewItemReadObserver creates a new instance of ItemReadObserver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewItemReadObserver(t mockConstructorTestingTNewItemReadObserver) *ItemReadObserver {
+}) *ItemReadObserver {
 	mock := &ItemReadObserver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/items/mocks/Items.go b/pkg/items/mocks/Items.go
index 1f6833ff8ac1db35c895fc30cf50968a0fcb4349..9bbd8948668a7ec665fdefd2f5e086cb235d579d 100644
--- a/pkg/items/mocks/Items.go
+++ b/pkg/items/mocks/Items.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.27.1. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -556,13 +556,12 @@ func (_m *Items) Update(ctx context.Context, item *items.Item, options ...*items
 	return r0
 }
 
-type mockConstructorTestingTNewItems interface {
+// NewItems creates a new instance of Items. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewItems(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewItems creates a new instance of Items. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewItems(t mockConstructorTestingTNewItems) *Items {
+}) *Items {
 	mock := &Items{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/items/mocks/Middleware.go b/pkg/items/mocks/Middleware.go
index 89adb97469b8df07f8acfc380339e5115ac21478..1cde36c075701f58cd108a677fc73537cf25bf52 100644
--- a/pkg/items/mocks/Middleware.go
+++ b/pkg/items/mocks/Middleware.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.27.1. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -29,13 +29,12 @@ func (_m *Middleware) Execute(_a0 items.Items) items.Items {
 	return r0
 }
 
-type mockConstructorTestingTNewMiddleware interface {
+// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMiddleware(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewMiddleware(t mockConstructorTestingTNewMiddleware) *Middleware {
+}) *Middleware {
 	mock := &Middleware{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/items/mocks/PreSaver.go b/pkg/items/mocks/PreSaver.go
index eb4e0cc2831e523db94d2c6881fef15b0353dc92..729c1ebfc4e7e60e2920b082e22f35617bf22e4e 100644
--- a/pkg/items/mocks/PreSaver.go
+++ b/pkg/items/mocks/PreSaver.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.27.1. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -49,13 +49,12 @@ func (_m *PreSaver) PreSave(ctx context.Context, f *field.Field, v interface{},
 	return r0, r1, r2
 }
 
-type mockConstructorTestingTNewPreSaver interface {
+// NewPreSaver creates a new instance of PreSaver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewPreSaver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewPreSaver creates a new instance of PreSaver. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewPreSaver(t mockConstructorTestingTNewPreSaver) *PreSaver {
+}) *PreSaver {
 	mock := &PreSaver{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/items/mocks/ProcessDataFunc.go b/pkg/items/mocks/ProcessDataFunc.go
index cb04ca90c304c51574dfdeb3a297bbd71fbbf80d..6ba38c984c7bba69461acc206393ebb66ba1cc07 100644
--- a/pkg/items/mocks/ProcessDataFunc.go
+++ b/pkg/items/mocks/ProcessDataFunc.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.27.1. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -41,13 +41,12 @@ func (_m *ProcessDataFunc) Execute(ctx context.Context, sch *schema.Schema, data
 	return r0, r1
 }
 
-type mockConstructorTestingTNewProcessDataFunc interface {
+// NewProcessDataFunc creates a new instance of ProcessDataFunc. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewProcessDataFunc(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewProcessDataFunc creates a new instance of ProcessDataFunc. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewProcessDataFunc(t mockConstructorTestingTNewProcessDataFunc) *ProcessDataFunc {
+}) *ProcessDataFunc {
 	mock := &ProcessDataFunc{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/items/mocks/Storage.go b/pkg/items/mocks/Storage.go
index 5630fc8b09c1a036a1580a6e1b85052ca93beecd..6f8082b98690dab97058c6775f3a53c2c655c80f 100644
--- a/pkg/items/mocks/Storage.go
+++ b/pkg/items/mocks/Storage.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.27.1. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -589,13 +589,12 @@ func (_m *Storage) Update(ctx context.Context, coll *collections.Collection, ite
 	return r0
 }
 
-type mockConstructorTestingTNewStorage interface {
+// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewStorage(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewStorage(t mockConstructorTestingTNewStorage) *Storage {
+}) *Storage {
 	mock := &Storage{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/members/mocks/Members.go b/pkg/members/mocks/Members.go
index 894bc76d5652c37559e1c77e6f0d290b3551497a..92a4f955147c04320a00c4df7193280022327dbf 100644
--- a/pkg/members/mocks/Members.go
+++ b/pkg/members/mocks/Members.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -19,13 +19,16 @@ func (_m *Members) Get(ctx context.Context, orgId string, userId string) (member
 	ret := _m.Called(ctx, orgId, userId)
 
 	var r0 members.Role
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) (members.Role, error)); ok {
+		return rf(ctx, orgId, userId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string) members.Role); ok {
 		r0 = rf(ctx, orgId, userId)
 	} else {
 		r0 = ret.Get(0).(members.Role)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
 		r1 = rf(ctx, orgId, userId)
 	} else {
@@ -40,6 +43,10 @@ func (_m *Members) ListMembers(ctx context.Context, orgId string) ([]*members.Me
 	ret := _m.Called(ctx, orgId)
 
 	var r0 []*members.Member
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) ([]*members.Member, error)); ok {
+		return rf(ctx, orgId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string) []*members.Member); ok {
 		r0 = rf(ctx, orgId)
 	} else {
@@ -48,7 +55,6 @@ func (_m *Members) ListMembers(ctx context.Context, orgId string) ([]*members.Me
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, orgId)
 	} else {
@@ -63,6 +69,10 @@ func (_m *Members) ListOrganizations(ctx context.Context, userId string) ([]*mem
 	ret := _m.Called(ctx, userId)
 
 	var r0 []*members.Member
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) ([]*members.Member, error)); ok {
+		return rf(ctx, userId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string) []*members.Member); ok {
 		r0 = rf(ctx, userId)
 	} else {
@@ -71,7 +81,6 @@ func (_m *Members) ListOrganizations(ctx context.Context, userId string) ([]*mem
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, userId)
 	} else {
@@ -123,13 +132,12 @@ func (_m *Members) Set(ctx context.Context, orgId string, userId string, role me
 	return r0
 }
 
-type mockConstructorTestingTNewMembers interface {
+// NewMembers creates a new instance of Members. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMembers(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewMembers creates a new instance of Members. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewMembers(t mockConstructorTestingTNewMembers) *Members {
+}) *Members {
 	mock := &Members{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/members/mocks/Middleware.go b/pkg/members/mocks/Middleware.go
index b953cf79d8239ef8da125e809bd29e1cf0a649f0..d32b22b62cbbd086862f53ce845d13dc89b552d7 100644
--- a/pkg/members/mocks/Middleware.go
+++ b/pkg/members/mocks/Middleware.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -28,13 +28,12 @@ func (_m *Middleware) Execute(_a0 observer.Observer) observer.Observer {
 	return r0
 }
 
-type mockConstructorTestingTNewMiddleware interface {
+// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMiddleware(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewMiddleware(t mockConstructorTestingTNewMiddleware) *Middleware {
+}) *Middleware {
 	mock := &Middleware{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/members/mocks/Observer.go b/pkg/members/mocks/Observer.go
index f2feb9607c89babdb3e27262a8095d47bc209873..8914e2119836d48d78069c4315d03ceafc083170 100644
--- a/pkg/members/mocks/Observer.go
+++ b/pkg/members/mocks/Observer.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -20,13 +20,16 @@ func (_m *Observer) OnCollaboratorSet(ctx context.Context, collaborator *collabo
 	ret := _m.Called(ctx, collaborator)
 
 	var r0 string
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.Collaborator) (string, error)); ok {
+		return rf(ctx, collaborator)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.Collaborator) string); ok {
 		r0 = rf(ctx, collaborator)
 	} else {
 		r0 = ret.Get(0).(string)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.Collaborator) error); ok {
 		r1 = rf(ctx, collaborator)
 	} else {
@@ -36,13 +39,12 @@ func (_m *Observer) OnCollaboratorSet(ctx context.Context, collaborator *collabo
 	return r0, r1
 }
 
-type mockConstructorTestingTNewObserver interface {
+// NewObserver creates a new instance of Observer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewObserver(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewObserver creates a new instance of Observer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewObserver(t mockConstructorTestingTNewObserver) *Observer {
+}) *Observer {
 	mock := &Observer{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/members/mocks/Storage.go b/pkg/members/mocks/Storage.go
index 1527b6e09fce71d7b35d84d04613d2dcbfa96249..6732079eaf193795802339d4dae24c176c304037 100644
--- a/pkg/members/mocks/Storage.go
+++ b/pkg/members/mocks/Storage.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -21,6 +21,10 @@ func (_m *Storage) Find(ctx context.Context, filter *members.Filter, opts *optio
 	ret := _m.Called(ctx, filter, opts)
 
 	var r0 []*members.Member
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.Filter, *options.FindOptions) ([]*members.Member, error)); ok {
+		return rf(ctx, filter, opts)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *members.Filter, *options.FindOptions) []*members.Member); ok {
 		r0 = rf(ctx, filter, opts)
 	} else {
@@ -29,7 +33,6 @@ func (_m *Storage) Find(ctx context.Context, filter *members.Filter, opts *optio
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *members.Filter, *options.FindOptions) error); ok {
 		r1 = rf(ctx, filter, opts)
 	} else {
@@ -58,13 +61,16 @@ func (_m *Storage) Remove(ctx context.Context, filter *members.Filter) (int, err
 	ret := _m.Called(ctx, filter)
 
 	var r0 int
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.Filter) (int, error)); ok {
+		return rf(ctx, filter)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *members.Filter) int); ok {
 		r0 = rf(ctx, filter)
 	} else {
 		r0 = ret.Get(0).(int)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *members.Filter) error); ok {
 		r1 = rf(ctx, filter)
 	} else {
@@ -102,13 +108,12 @@ func (_m *Storage) Set(ctx context.Context, orgID string, userID string, role me
 	return r0
 }
 
-type mockConstructorTestingTNewStorage interface {
+// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewStorage(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewStorage(t mockConstructorTestingTNewStorage) *Storage {
+}) *Storage {
 	mock := &Storage{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/operation/operation.go b/pkg/operation/operation.go
new file mode 100644
index 0000000000000000000000000000000000000000..bbe73370c21ce1a0c290fd53cd8a2225ba62cc4a
--- /dev/null
+++ b/pkg/operation/operation.go
@@ -0,0 +1,216 @@
+package operation
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	grpcerr "git.perx.ru/perxis/perxis-go/pkg/errors/grpc"
+	"git.perx.ru/perxis/perxis-go/pkg/id"
+	"git.perx.ru/perxis/perxis-go/proto/common"
+	"google.golang.org/grpc"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/types/known/anypb"
+	"google.golang.org/protobuf/types/known/timestamppb"
+)
+
+// DefaultPollInterval интервал опроса операции по умолчанию
+const DefaultPollInterval = time.Second
+
+type Client = common.OperationServiceClient
+type Proto = common.Operation
+
+func NewClient(conn *grpc.ClientConn) Client {
+	return common.NewOperationServiceClient(conn)
+}
+
+// Operation обертка над `common.Operation` для работы с продолжительными операциями.
+// Позволяет получить результат операции, проверить статус, отменить операцию
+type Operation struct {
+	proto  *Proto
+	client Client
+
+	retention  time.Duration
+	cancelFunc func()
+}
+
+// New создает новую операцию
+func New(proto *Proto) *Operation {
+	o := &Operation{
+		proto: proto,
+	}
+	o.proto.Id = id.GenerateNewID()
+	o.proto.CreatedAt = timestamppb.New(time.Now())
+	return o
+}
+
+// Wrap оборачивает `common.Operation` в Operation для работы c сервисом поддерживающим продолжительных операций
+func Wrap(client Client, proto *Proto) *Operation {
+	if proto == nil || proto.GetId() == "" {
+		return nil
+	}
+
+	return &Operation{
+		proto:  proto,
+		client: client,
+	}
+}
+
+// String возвращает строковое представление операции
+func (o *Operation) String() string {
+	return fmt.Sprintf("Operation #%s (%s, %s) Done:%t", o.Id(), o.CreatedAt(), o.CreatedBy(), o.IsDone())
+}
+
+// Id возвращает идентификатор операции
+func (o *Operation) Id() string {
+	return o.proto.GetId()
+}
+
+// Proto возвращает `common.Operation`
+func (o *Operation) Proto() *Proto {
+	return o.proto
+}
+
+// Description возвращает описание операции
+func (o *Operation) Description() string {
+	return o.proto.GetDescription()
+}
+
+// CreatedAt возвращает время создания операции
+func (o *Operation) CreatedAt() time.Time {
+	c := o.proto.GetCreatedAt()
+	if c != nil {
+		return c.AsTime()
+	}
+	return time.Time{}
+}
+
+// IsDone возвращает true если операция завершена
+func (o *Operation) IsDone() bool {
+	return o.proto.GetDone()
+}
+
+// Response возвращает результат операции
+func (o *Operation) Response() (proto.Message, error) {
+	r := o.proto.GetResponse()
+	if r == nil {
+		return nil, nil
+	}
+	return r.UnmarshalNew()
+}
+
+// CreatedBy возвращает имя пользователя создавшего операцию
+func (o *Operation) CreatedBy() string {
+	return o.proto.GetCreatedBy()
+}
+
+// SetCreatedBy устанавливает описание операции
+func (o *Operation) SetCreatedBy(createdBy string) {
+	o.proto.CreatedBy = createdBy
+}
+
+// SetCreatedAt устанавливает время создания операции
+func (o *Operation) SetCreatedAt(t time.Time) {
+	o.proto.CreatedAt = timestamppb.New(t)
+}
+
+// IsExpired возвращает true если операция просрочена
+func (o *Operation) IsExpired() bool {
+	createdAt := o.CreatedAt()
+	if o.retention != 0 && !createdAt.IsZero() && time.Since(createdAt) > o.retention {
+		return true
+	}
+	return false
+}
+
+func (o *Operation) SetModifiedAt(t time.Time) {
+	o.proto.ModifiedAt = timestamppb.New(t)
+}
+
+// SetResponse устанавливает результат операции
+func (o *Operation) SetResponse(m proto.Message) error {
+	resp, err := anypb.New(m)
+	if err != nil {
+		return err
+	}
+
+	o.proto.Result = &common.Operation_Response{Response: resp}
+	o.proto.Done = true
+	return nil
+}
+
+// SetCancelFunc устанавливает функцию отмены операции
+func (o *Operation) SetCancelFunc(cancelFunc func()) {
+	o.cancelFunc = cancelFunc
+}
+
+// SetError устанавливает ошибку операции
+func (o *Operation) SetError(err error) {
+	o.proto.Result = &common.Operation_Error{Error: grpcerr.ProtoFromError(err)}
+}
+
+// Error возвращает ошибку операции
+func (o *Operation) Error() error {
+	return grpcerr.ErrorFromProto(nil, o.proto.GetError())
+}
+
+func (o *Operation) SetDone(done bool) {
+	o.proto.Done = done
+}
+
+// SetRetention устанавливает время хранения операции
+func (o *Operation) SetRetention(retention time.Duration) {
+	o.retention = retention
+}
+
+// Cancel отменяет операцию
+func (o *Operation) Cancel() error {
+	req := &common.CancelOperationRequest{OperationId: o.Id()}
+	op, err := o.client.Cancel(context.Background(), req)
+	if err != nil {
+		return err
+	}
+	o.proto = op
+	return nil
+}
+
+// Poll запрашивает состояние операции
+func (o *Operation) Poll(ctx context.Context, opts ...grpc.CallOption) error {
+	req := &common.GetOperationRequest{OperationId: o.Id()}
+	op, err := o.client.Get(ctx, req, opts...)
+	if err != nil {
+		return err
+	}
+	o.proto = op
+	return nil
+}
+
+// Wait ожидает завершения операции
+func (o *Operation) Wait(ctx context.Context, opts ...grpc.CallOption) error {
+	return o.WaitInterval(ctx, DefaultPollInterval, opts...)
+}
+
+// WaitInterval ожидает завершения операции с указанным интервалом опроса
+func (o *Operation) WaitInterval(ctx context.Context, pollInterval time.Duration, opts ...grpc.CallOption) error {
+	for !o.IsDone() {
+		if err := o.Poll(ctx, opts...); err != nil {
+			return errors.Wrap(err, "operation poll failed")
+		}
+
+		if o.IsDone() {
+			break
+		}
+
+		interval := pollInterval
+		timer := time.NewTimer(interval)
+		select {
+		case <-timer.C:
+		case <-ctx.Done():
+			timer.Stop()
+			return errors.Wrap(ctx.Err(), "operation wait timeout")
+		}
+	}
+
+	return errors.Wrap(o.Error(), "operation failed")
+}
diff --git a/pkg/operation/operation_test.go b/pkg/operation/operation_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b59731708c60034447facc10dee3c248d5b1b383
--- /dev/null
+++ b/pkg/operation/operation_test.go
@@ -0,0 +1,94 @@
+package operation
+
+import (
+	"context"
+	"fmt"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+
+	"git.perx.ru/perxis/perxis-go/proto/common"
+
+	"github.com/stretchr/testify/mock"
+
+	"git.perx.ru/perxis/perxis-go/proto/mocks"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestOperation(t *testing.T) {
+	op := New(&Proto{})
+	require.NotNil(t, op)
+	assert.NotEmpty(t, op.Id())
+	assert.False(t, op.IsExpired())
+	assert.False(t, op.IsDone())
+	assert.Less(t, time.Since(op.CreatedAt()), time.Second)
+
+	op.SetRetention(time.Nanosecond)
+	assert.True(t, op.IsExpired())
+
+	op.SetCancelFunc(func() {
+		// do nothing
+	})
+}
+
+func TestOperation_Wait(t *testing.T) {
+	var count int
+	client := &mocks.OperationServiceClient{}
+	call := client.On("Get", mock.Anything, mock.Anything, mock.Anything)
+	call.Run(func(args mock.Arguments) {
+		r := args.Get(1).(*common.GetOperationRequest)
+		require.Equal(t, "test", r.OperationId)
+		p := &Proto{
+			Id:          "test",
+			Description: fmt.Sprintf("count=%d", count),
+		}
+
+		if count == 10 {
+			p.Done = true
+		}
+
+		call.Return(p, nil)
+		count++
+	})
+
+	t.Run("Ok", func(t *testing.T) {
+		count = 0
+		op := Wrap(client, &Proto{
+			Id: "test",
+		})
+
+		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+		defer cancel()
+
+		err := op.WaitInterval(ctx, time.Millisecond*10)
+		assert.NoError(t, err)
+	})
+
+	t.Run("Timeout", func(t *testing.T) {
+		count = 0
+		op := Wrap(client, &Proto{
+			Id: "test",
+		})
+
+		ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*30)
+		defer cancel()
+
+		err := op.WaitInterval(ctx, time.Millisecond*10)
+		assert.ErrorIs(t, err, context.DeadlineExceeded)
+	})
+
+	t.Run("Cancel", func(t *testing.T) {
+		count = 0
+		op := Wrap(client, &Proto{
+			Id: "test",
+		})
+
+		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
+		time.AfterFunc(time.Millisecond*30, cancel)
+
+		err := op.WaitInterval(ctx, time.Millisecond*10)
+		assert.ErrorIs(t, err, context.Canceled)
+	})
+}
diff --git a/pkg/operation/server.go b/pkg/operation/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..1106ed3360aff0b8777b5f3272134296c700990b
--- /dev/null
+++ b/pkg/operation/server.go
@@ -0,0 +1,34 @@
+package operation
+
+import (
+	"context"
+
+	"git.perx.ru/perxis/perxis-go/proto/common"
+)
+
+type server struct {
+	svc Service
+	common.UnimplementedOperationServiceServer
+}
+
+func NewServer(svc Service) common.OperationServiceServer {
+	return &server{
+		svc: svc,
+	}
+}
+
+func (s *server) Get(ctx context.Context, req *common.GetOperationRequest) (*common.Operation, error) {
+	op, err := s.svc.Get(ctx, req.GetOperationId())
+	if err != nil {
+		return nil, err
+	}
+	return op.Proto(), nil
+}
+
+func (s *server) Cancel(ctx context.Context, req *common.CancelOperationRequest) (*common.Operation, error) {
+	op, err := s.svc.Cancel(ctx, req.GetOperationId())
+	if err != nil {
+		return nil, err
+	}
+	return op.Proto(), nil
+}
diff --git a/pkg/operation/service.go b/pkg/operation/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..7561b52ac3ef0e7df521164b32e247a3c96b30f2
--- /dev/null
+++ b/pkg/operation/service.go
@@ -0,0 +1,148 @@
+package operation
+
+import (
+	"context"
+	"sync"
+	"time"
+
+	"git.perx.ru/perxis/perxis-go/pkg/auth"
+	"git.perx.ru/perxis/perxis-go/pkg/errors"
+	"google.golang.org/protobuf/proto"
+)
+
+const (
+	DefaultRetention       = time.Hour
+	DefaultCleanupInterval = time.Minute
+)
+
+// Service интерфейс сервиса для работы с продолжительными операциями
+type Service interface {
+
+	// Create создает новую операцию и запускает ее в фоне
+	Create(ctx context.Context, desc string, fn func(ctx context.Context) (proto.Message, error)) (*Operation, error)
+
+	// Set сохраняет операцию в хранилище
+	Set(ctx context.Context, op *Operation) error
+
+	// Get возвращает операцию по ее идентификатору
+	Get(ctx context.Context, id string) (*Operation, error)
+
+	// Cancel отменяет операцию по ее идентификатору
+	Cancel(ctx context.Context, id string) (*Operation, error)
+
+	// Cleanup удаляет просроченные операции
+	Cleanup()
+}
+
+type service struct {
+	ops             map[string]*Operation // Registry of operations
+	mu              sync.RWMutex          // Mutex for operations registry
+	lastCleanup     time.Time             // Last time when cleanup was performed
+	cleanupInterval time.Duration         // Cleanup interval
+}
+
+func NewService(cleanupInterval time.Duration) Service {
+	return &service{
+		ops:             make(map[string]*Operation),
+		mu:              sync.RWMutex{},
+		cleanupInterval: cleanupInterval,
+	}
+}
+
+func NewDefaultService() Service {
+	return &service{
+		ops:             make(map[string]*Operation),
+		mu:              sync.RWMutex{},
+		cleanupInterval: DefaultCleanupInterval,
+	}
+}
+
+func (s *service) Cleanup() {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	for id, op := range s.ops {
+		if op.IsExpired() {
+			delete(s.ops, id)
+		}
+	}
+}
+
+func (s *service) CleanupIfNeeded() {
+	if time.Since(s.lastCleanup) > s.cleanupInterval {
+		s.lastCleanup = time.Now()
+		s.Cleanup()
+	}
+}
+
+// Set сохраняет операцию в хранилище
+func (s *service) Set(_ context.Context, op *Operation) error {
+	if op == nil || op.Id() == "" {
+		return errors.New("invalid operation")
+	}
+
+	if op.retention == 0 {
+		op.retention = DefaultRetention
+	}
+
+	s.mu.Lock()
+	s.ops[op.Id()] = op
+	s.mu.Unlock()
+
+	s.CleanupIfNeeded()
+	return nil
+}
+
+// Get возвращает операцию по ее идентификатору
+func (s *service) Get(_ context.Context, id string) (*Operation, error) {
+	s.mu.RLock()
+	defer s.mu.RUnlock()
+	op, ok := s.ops[id]
+	if !ok {
+		return nil, errors.New("operation not found")
+	}
+	return op, nil
+}
+
+// Cancel отменяет операцию по ее идентификатору
+func (s *service) Cancel(_ context.Context, id string) (*Operation, error) {
+	s.mu.RLock()
+	defer s.mu.Unlock()
+	op, ok := s.ops[id]
+	if !ok {
+		return nil, errors.New("operation not found")
+	}
+
+	if op.cancelFunc != nil {
+		op.cancelFunc()
+	}
+
+	return op, nil
+}
+
+func (s *service) Create(ctx context.Context, desc string, fn func(ctx context.Context) (proto.Message, error)) (*Operation, error) {
+	principal := auth.GetPrincipal(ctx)
+	op := New(&Proto{
+		Description: desc,
+		CreatedBy:   principal.GetID(ctx),
+	})
+
+	// Создаем новый контекст для фоновой операции без Deadline
+	opCtx, cancel := context.WithCancel(auth.WithPrincipal(context.Background(), principal))
+	op.SetCancelFunc(cancel)
+	s.Set(opCtx, op)
+
+	go func() {
+		response, err := fn(opCtx)
+		if err != nil {
+			op.SetError(err)
+		}
+		if response != nil {
+			op.SetResponse(response)
+		}
+		op.SetModifiedAt(time.Now())
+		op.SetDone(true)
+		s.Set(opCtx, op)
+	}()
+
+	return op, nil
+}
diff --git a/pkg/roles/mocks/Middleware.go b/pkg/roles/mocks/Middleware.go
index 35f4f9798cdb83f05ded631cf0b23f1815fe0b47..507b37154a40a1176a940bd29b6a6d23556a6bba 100644
--- a/pkg/roles/mocks/Middleware.go
+++ b/pkg/roles/mocks/Middleware.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -28,13 +28,12 @@ func (_m *Middleware) Execute(_a0 roles.Roles) roles.Roles {
 	return r0
 }
 
-type mockConstructorTestingTNewMiddleware interface {
+// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMiddleware(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewMiddleware(t mockConstructorTestingTNewMiddleware) *Middleware {
+}) *Middleware {
 	mock := &Middleware{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/roles/mocks/Roles.go b/pkg/roles/mocks/Roles.go
index 1fc212541053dcfa1a05bdcd2d0b528d3f95fc34..08c523e06bc5082685ea1f7669a4b15849712222 100644
--- a/pkg/roles/mocks/Roles.go
+++ b/pkg/roles/mocks/Roles.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -19,6 +19,10 @@ func (_m *Roles) Create(ctx context.Context, role *roles.Role) (*roles.Role, err
 	ret := _m.Called(ctx, role)
 
 	var r0 *roles.Role
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.Role) (*roles.Role, error)); ok {
+		return rf(ctx, role)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *roles.Role) *roles.Role); ok {
 		r0 = rf(ctx, role)
 	} else {
@@ -27,7 +31,6 @@ func (_m *Roles) Create(ctx context.Context, role *roles.Role) (*roles.Role, err
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *roles.Role) error); ok {
 		r1 = rf(ctx, role)
 	} else {
@@ -56,6 +59,10 @@ func (_m *Roles) Get(ctx context.Context, spaceId string, roleId string) (*roles
 	ret := _m.Called(ctx, spaceId, roleId)
 
 	var r0 *roles.Role
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, string) (*roles.Role, error)); ok {
+		return rf(ctx, spaceId, roleId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, string) *roles.Role); ok {
 		r0 = rf(ctx, spaceId, roleId)
 	} else {
@@ -64,7 +71,6 @@ func (_m *Roles) Get(ctx context.Context, spaceId string, roleId string) (*roles
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok {
 		r1 = rf(ctx, spaceId, roleId)
 	} else {
@@ -79,6 +85,10 @@ func (_m *Roles) List(ctx context.Context, spaceId string) ([]*roles.Role, error
 	ret := _m.Called(ctx, spaceId)
 
 	var r0 []*roles.Role
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) ([]*roles.Role, error)); ok {
+		return rf(ctx, spaceId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string) []*roles.Role); ok {
 		r0 = rf(ctx, spaceId)
 	} else {
@@ -87,7 +97,6 @@ func (_m *Roles) List(ctx context.Context, spaceId string) ([]*roles.Role, error
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, spaceId)
 	} else {
@@ -111,13 +120,12 @@ func (_m *Roles) Update(ctx context.Context, role *roles.Role) error {
 	return r0
 }
 
-type mockConstructorTestingTNewRoles interface {
+// NewRoles creates a new instance of Roles. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewRoles(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewRoles creates a new instance of Roles. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewRoles(t mockConstructorTestingTNewRoles) *Roles {
+}) *Roles {
 	mock := &Roles{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/roles/mocks/Storage.go b/pkg/roles/mocks/Storage.go
index d2d525a19e2c10096a0a1222122e302ad5ea0ce7..dc01f165aa37163cf23f28302e9f90f72420171e 100644
--- a/pkg/roles/mocks/Storage.go
+++ b/pkg/roles/mocks/Storage.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -21,6 +21,10 @@ func (_m *Storage) Create(ctx context.Context, role *roles.Role) (*roles.Role, e
 	ret := _m.Called(ctx, role)
 
 	var r0 *roles.Role
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.Role) (*roles.Role, error)); ok {
+		return rf(ctx, role)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *roles.Role) *roles.Role); ok {
 		r0 = rf(ctx, role)
 	} else {
@@ -29,7 +33,6 @@ func (_m *Storage) Create(ctx context.Context, role *roles.Role) (*roles.Role, e
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *roles.Role) error); ok {
 		r1 = rf(ctx, role)
 	} else {
@@ -58,6 +61,11 @@ func (_m *Storage) Find(ctx context.Context, spaceID string, f *roles.Filter, op
 	ret := _m.Called(ctx, spaceID, f, opts)
 
 	var r0 []*roles.Role
+	var r1 int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(context.Context, string, *roles.Filter, *options.FindOptions) ([]*roles.Role, int, error)); ok {
+		return rf(ctx, spaceID, f, opts)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string, *roles.Filter, *options.FindOptions) []*roles.Role); ok {
 		r0 = rf(ctx, spaceID, f, opts)
 	} else {
@@ -66,14 +74,12 @@ func (_m *Storage) Find(ctx context.Context, spaceID string, f *roles.Filter, op
 		}
 	}
 
-	var r1 int
 	if rf, ok := ret.Get(1).(func(context.Context, string, *roles.Filter, *options.FindOptions) int); ok {
 		r1 = rf(ctx, spaceID, f, opts)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	var r2 error
 	if rf, ok := ret.Get(2).(func(context.Context, string, *roles.Filter, *options.FindOptions) error); ok {
 		r2 = rf(ctx, spaceID, f, opts)
 	} else {
@@ -102,20 +108,23 @@ func (_m *Storage) Update(ctx context.Context, role *roles.Role) (int, int, erro
 	ret := _m.Called(ctx, role)
 
 	var r0 int
+	var r1 int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.Role) (int, int, error)); ok {
+		return rf(ctx, role)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *roles.Role) int); ok {
 		r0 = rf(ctx, role)
 	} else {
 		r0 = ret.Get(0).(int)
 	}
 
-	var r1 int
 	if rf, ok := ret.Get(1).(func(context.Context, *roles.Role) int); ok {
 		r1 = rf(ctx, role)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	var r2 error
 	if rf, ok := ret.Get(2).(func(context.Context, *roles.Role) error); ok {
 		r2 = rf(ctx, role)
 	} else {
@@ -125,13 +134,12 @@ func (_m *Storage) Update(ctx context.Context, role *roles.Role) (int, int, erro
 	return r0, r1, r2
 }
 
-type mockConstructorTestingTNewStorage interface {
+// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewStorage(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewStorage(t mockConstructorTestingTNewStorage) *Storage {
+}) *Storage {
 	mock := &Storage{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/setup/collection.go b/pkg/setup/collection.go
index 3b22ecf87118307d58ec942a5abef47aa439679b..a42c650d67f0d9290490ca157b55fbcce6c8c344 100644
--- a/pkg/setup/collection.go
+++ b/pkg/setup/collection.go
@@ -22,9 +22,10 @@ type UpdateCollectionFn func(s *Setup, exist, new *collections.Collection) (coll
 type DeleteCollectionFn func(s *Setup, col *collections.Collection) (bool, error)
 
 type CollectionConfig struct {
-	collection *collections.Collection
-	UpdateFn   UpdateCollectionFn
-	DeleteFn   DeleteCollectionFn
+	collection    *collections.Collection
+	UpdateFn      UpdateCollectionFn
+	DeleteFn      DeleteCollectionFn
+	SkipMigration bool
 }
 
 func NewCollectionConfig(collection *collections.Collection, opt ...CollectionsOption) CollectionConfig {
@@ -40,6 +41,12 @@ func NewCollectionConfig(collection *collections.Collection, opt ...CollectionsO
 	return c
 }
 
+func SkipMigration() CollectionsOption {
+	return func(c *CollectionConfig) {
+		c.SkipMigration = true
+	}
+}
+
 func OverwriteCollection() CollectionsOption {
 	return func(c *CollectionConfig) {
 		c.UpdateFn = func(s *Setup, old, new *collections.Collection) (*collections.Collection, bool, bool, error) {
@@ -120,7 +127,7 @@ func (s *Setup) InstallCollections(ctx context.Context) (err error) {
 			)
 			return errors.WithDetailf(errors.Wrap(err, "failed to install collection"), "Возникла ошибка при настройке коллекции %s(%s)", c.collection.Name, c.collection.ID)
 		}
-		if setSchema {
+		if !c.SkipMigration && setSchema {
 			migrate = true
 		}
 	}
diff --git a/pkg/users/mocks/Middleware.go b/pkg/users/mocks/Middleware.go
index 7a8e59c7ce2ef43db52d15457ac0c57494bb383a..63d457eb9c26bfa2b297a44ba994cc2be35e2055 100644
--- a/pkg/users/mocks/Middleware.go
+++ b/pkg/users/mocks/Middleware.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -28,13 +28,12 @@ func (_m *Middleware) Execute(_a0 users.Users) users.Users {
 	return r0
 }
 
-type mockConstructorTestingTNewMiddleware interface {
+// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMiddleware(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewMiddleware creates a new instance of Middleware. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewMiddleware(t mockConstructorTestingTNewMiddleware) *Middleware {
+}) *Middleware {
 	mock := &Middleware{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/users/mocks/Storage.go b/pkg/users/mocks/Storage.go
index e918d42414221cc2b6f29abe27116777714df08a..a1001d78cacac9825e7a5cfdfc7f0a712a661d8a 100644
--- a/pkg/users/mocks/Storage.go
+++ b/pkg/users/mocks/Storage.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -21,6 +21,10 @@ func (_m *Storage) Create(ctx context.Context, create *users.User) (*users.User,
 	ret := _m.Called(ctx, create)
 
 	var r0 *users.User
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.User) (*users.User, error)); ok {
+		return rf(ctx, create)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *users.User) *users.User); ok {
 		r0 = rf(ctx, create)
 	} else {
@@ -29,7 +33,6 @@ func (_m *Storage) Create(ctx context.Context, create *users.User) (*users.User,
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *users.User) error); ok {
 		r1 = rf(ctx, create)
 	} else {
@@ -44,13 +47,16 @@ func (_m *Storage) Delete(ctx context.Context, filter *users.Filter) (int, error
 	ret := _m.Called(ctx, filter)
 
 	var r0 int
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.Filter) (int, error)); ok {
+		return rf(ctx, filter)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *users.Filter) int); ok {
 		r0 = rf(ctx, filter)
 	} else {
 		r0 = ret.Get(0).(int)
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *users.Filter) error); ok {
 		r1 = rf(ctx, filter)
 	} else {
@@ -65,6 +71,11 @@ func (_m *Storage) Find(ctx context.Context, filter *users.Filter, opts *options
 	ret := _m.Called(ctx, filter, opts)
 
 	var r0 []*users.User
+	var r1 int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.Filter, *options.FindOptions) ([]*users.User, int, error)); ok {
+		return rf(ctx, filter, opts)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *users.Filter, *options.FindOptions) []*users.User); ok {
 		r0 = rf(ctx, filter, opts)
 	} else {
@@ -73,14 +84,12 @@ func (_m *Storage) Find(ctx context.Context, filter *users.Filter, opts *options
 		}
 	}
 
-	var r1 int
 	if rf, ok := ret.Get(1).(func(context.Context, *users.Filter, *options.FindOptions) int); ok {
 		r1 = rf(ctx, filter, opts)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	var r2 error
 	if rf, ok := ret.Get(2).(func(context.Context, *users.Filter, *options.FindOptions) error); ok {
 		r2 = rf(ctx, filter, opts)
 	} else {
@@ -123,20 +132,23 @@ func (_m *Storage) Update(ctx context.Context, update *users.User, filter *users
 	ret := _m.Called(ctx, update, filter)
 
 	var r0 int
+	var r1 int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.User, *users.Filter) (int, int, error)); ok {
+		return rf(ctx, update, filter)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *users.User, *users.Filter) int); ok {
 		r0 = rf(ctx, update, filter)
 	} else {
 		r0 = ret.Get(0).(int)
 	}
 
-	var r1 int
 	if rf, ok := ret.Get(1).(func(context.Context, *users.User, *users.Filter) int); ok {
 		r1 = rf(ctx, update, filter)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	var r2 error
 	if rf, ok := ret.Get(2).(func(context.Context, *users.User, *users.Filter) error); ok {
 		r2 = rf(ctx, update, filter)
 	} else {
@@ -146,13 +158,12 @@ func (_m *Storage) Update(ctx context.Context, update *users.User, filter *users
 	return r0, r1, r2
 }
 
-type mockConstructorTestingTNewStorage interface {
+// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewStorage(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewStorage creates a new instance of Storage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewStorage(t mockConstructorTestingTNewStorage) *Storage {
+}) *Storage {
 	mock := &Storage{}
 	mock.Mock.Test(t)
 
diff --git a/pkg/users/mocks/Users.go b/pkg/users/mocks/Users.go
index 124cc3c5096fbd00a9554c5e182610236e162574..4db80b6d31f883092ab690f2d4ffe1616a339d35 100644
--- a/pkg/users/mocks/Users.go
+++ b/pkg/users/mocks/Users.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.15.0. DO NOT EDIT.
+// Code generated by mockery v2.33.3. DO NOT EDIT.
 
 package mocks
 
@@ -21,6 +21,10 @@ func (_m *Users) Create(ctx context.Context, create *users.User) (*users.User, e
 	ret := _m.Called(ctx, create)
 
 	var r0 *users.User
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.User) (*users.User, error)); ok {
+		return rf(ctx, create)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *users.User) *users.User); ok {
 		r0 = rf(ctx, create)
 	} else {
@@ -29,7 +33,6 @@ func (_m *Users) Create(ctx context.Context, create *users.User) (*users.User, e
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, *users.User) error); ok {
 		r1 = rf(ctx, create)
 	} else {
@@ -58,6 +61,11 @@ func (_m *Users) Find(ctx context.Context, filter *users.Filter, _a2 *options.Fi
 	ret := _m.Called(ctx, filter, _a2)
 
 	var r0 []*users.User
+	var r1 int
+	var r2 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.Filter, *options.FindOptions) ([]*users.User, int, error)); ok {
+		return rf(ctx, filter, _a2)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, *users.Filter, *options.FindOptions) []*users.User); ok {
 		r0 = rf(ctx, filter, _a2)
 	} else {
@@ -66,14 +74,12 @@ func (_m *Users) Find(ctx context.Context, filter *users.Filter, _a2 *options.Fi
 		}
 	}
 
-	var r1 int
 	if rf, ok := ret.Get(1).(func(context.Context, *users.Filter, *options.FindOptions) int); ok {
 		r1 = rf(ctx, filter, _a2)
 	} else {
 		r1 = ret.Get(1).(int)
 	}
 
-	var r2 error
 	if rf, ok := ret.Get(2).(func(context.Context, *users.Filter, *options.FindOptions) error); ok {
 		r2 = rf(ctx, filter, _a2)
 	} else {
@@ -88,6 +94,10 @@ func (_m *Users) Get(ctx context.Context, userId string) (*users.User, error) {
 	ret := _m.Called(ctx, userId)
 
 	var r0 *users.User
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) (*users.User, error)); ok {
+		return rf(ctx, userId)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string) *users.User); ok {
 		r0 = rf(ctx, userId)
 	} else {
@@ -96,7 +106,6 @@ func (_m *Users) Get(ctx context.Context, userId string) (*users.User, error) {
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, userId)
 	} else {
@@ -111,6 +120,10 @@ func (_m *Users) GetByIdentity(ctx context.Context, identity string) (*users.Use
 	ret := _m.Called(ctx, identity)
 
 	var r0 *users.User
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, string) (*users.User, error)); ok {
+		return rf(ctx, identity)
+	}
 	if rf, ok := ret.Get(0).(func(context.Context, string) *users.User); ok {
 		r0 = rf(ctx, identity)
 	} else {
@@ -119,7 +132,6 @@ func (_m *Users) GetByIdentity(ctx context.Context, identity string) (*users.Use
 		}
 	}
 
-	var r1 error
 	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
 		r1 = rf(ctx, identity)
 	} else {
@@ -143,13 +155,12 @@ func (_m *Users) Update(ctx context.Context, update *users.User) error {
 	return r0
 }
 
-type mockConstructorTestingTNewUsers interface {
+// NewUsers creates a new instance of Users. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUsers(t interface {
 	mock.TestingT
 	Cleanup(func())
-}
-
-// NewUsers creates a new instance of Users. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
-func NewUsers(t mockConstructorTestingTNewUsers) *Users {
+}) *Users {
 	mock := &Users{}
 	mock.Mock.Test(t)
 
diff --git a/proto/clients/clients.pb.go b/proto/clients/clients.pb.go
index 4c75762d6119e199e3113a4086a198f102ef09aa..a492dbad2bd2bfa0dd320e869f28f9b1ea5146fd 100644
--- a/proto/clients/clients.pb.go
+++ b/proto/clients/clients.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.31.0
-// 	protoc        v4.23.4
+// 	protoc        v4.24.3
 // source: clients/clients.proto
 
 package clients
diff --git a/proto/clients/clients_grpc.pb.go b/proto/clients/clients_grpc.pb.go
index 9b701db881a10acc1021de4e49cbf3b5ff4acd8a..f8fdf0cda264fa6e99aa20ecf495050dc1231fc7 100644
--- a/proto/clients/clients_grpc.pb.go
+++ b/proto/clients/clients_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v4.23.4
+// - protoc             v4.24.3
 // source: clients/clients.proto
 
 package clients
diff --git a/proto/collaborators/collaborators.pb.go b/proto/collaborators/collaborators.pb.go
index 0e35a00e51a759c48db1aae8e683f04083e7c672..e07247b5dce02708390f23a576462dcb965b772d 100644
--- a/proto/collaborators/collaborators.pb.go
+++ b/proto/collaborators/collaborators.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.29.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: collaborators/collaborators.proto
 
 package collaborators
diff --git a/proto/collaborators/collaborators_grpc.pb.go b/proto/collaborators/collaborators_grpc.pb.go
index 71a9d8a059f7851368d6a4467e2962657ef648a4..47ff522c1b57c72665fbca4741f698e6bb97b80f 100644
--- a/proto/collaborators/collaborators_grpc.pb.go
+++ b/proto/collaborators/collaborators_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v3.21.12
+// - protoc             v4.24.3
 // source: collaborators/collaborators.proto
 
 package collaborators
diff --git a/proto/collections/collections.pb.go b/proto/collections/collections.pb.go
index 71712728599d799f2bbeb975196a8fc75b77e042..225e67abf5eb63d4fd92fbe1593ebb6d645f6280 100644
--- a/proto/collections/collections.pb.go
+++ b/proto/collections/collections.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.31.0
-// 	protoc        v4.23.4
+// 	protoc        v4.24.3
 // source: collections/collections.proto
 
 package collections
diff --git a/proto/collections/collections_grpc.pb.go b/proto/collections/collections_grpc.pb.go
index eef27369eed0b0501ee45365e6195e4c52ea85c5..f0ccc6b2725c15048c8bfa812a5ea6f42aa8e2e4 100644
--- a/proto/collections/collections_grpc.pb.go
+++ b/proto/collections/collections_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v4.23.4
+// - protoc             v4.24.3
 // source: collections/collections.proto
 
 package collections
diff --git a/proto/common/common.pb.go b/proto/common/common.pb.go
index c0c2391032978279a1e7fa3c46d5bc1d9bb11eb6..867831c7239c004b5235ec15b844e9e171ccac9d 100644
--- a/proto/common/common.pb.go
+++ b/proto/common/common.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.29.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: common/common.proto
 
 package common
@@ -125,117 +125,6 @@ func (Action) EnumDescriptor() ([]byte, []int) {
 	return file_common_common_proto_rawDescGZIP(), []int{1}
 }
 
-type Error struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	ErrorCode         uint64                    `protobuf:"varint,1,opt,name=error_code,json=errorCode,proto3" json:"error_code,omitempty"`                                                                     // Код ошибки
-	ErrorId           string                    `protobuf:"bytes,2,opt,name=error_id,json=errorId,proto3" json:"error_id,omitempty"`                                                                            // ID конкретного инцидента, ID уникальна для каждого случая.
-	Reason            string                    `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"`                                                                                             // ???
-	Domain            string                    `protobuf:"bytes,4,opt,name=domain,proto3" json:"domain,omitempty"`                                                                                             // Сервис,Пакет к которому относится ошибка
-	Metadata          map[string]string         `protobuf:"bytes,9,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Мета-информация
-	BadRequest        *Error_BadRequest         `protobuf:"bytes,10,opt,name=bad_request,json=badRequest,proto3" json:"bad_request,omitempty"`                                                                  // Ошибка запроса
-	DebugInfo         *Error_DebugInfo          `protobuf:"bytes,11,opt,name=debug_info,json=debugInfo,proto3" json:"debug_info,omitempty"`                                                                     // Отладочная информация
-	Help              *Error_Help               `protobuf:"bytes,100,opt,name=help,proto3" json:"help,omitempty"`                                                                                               // Пользовательская информации и инструкции
-	LocalizedMessages []*Error_LocalizedMessage `protobuf:"bytes,200,rep,name=localized_messages,json=localizedMessages,proto3" json:"localized_messages,omitempty"`                                            // Перевод сообщения об ошибке
-}
-
-func (x *Error) Reset() {
-	*x = Error{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_common_common_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Error) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Error) ProtoMessage() {}
-
-func (x *Error) ProtoReflect() protoreflect.Message {
-	mi := &file_common_common_proto_msgTypes[0]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use Error.ProtoReflect.Descriptor instead.
-func (*Error) Descriptor() ([]byte, []int) {
-	return file_common_common_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *Error) GetErrorCode() uint64 {
-	if x != nil {
-		return x.ErrorCode
-	}
-	return 0
-}
-
-func (x *Error) GetErrorId() string {
-	if x != nil {
-		return x.ErrorId
-	}
-	return ""
-}
-
-func (x *Error) GetReason() string {
-	if x != nil {
-		return x.Reason
-	}
-	return ""
-}
-
-func (x *Error) GetDomain() string {
-	if x != nil {
-		return x.Domain
-	}
-	return ""
-}
-
-func (x *Error) GetMetadata() map[string]string {
-	if x != nil {
-		return x.Metadata
-	}
-	return nil
-}
-
-func (x *Error) GetBadRequest() *Error_BadRequest {
-	if x != nil {
-		return x.BadRequest
-	}
-	return nil
-}
-
-func (x *Error) GetDebugInfo() *Error_DebugInfo {
-	if x != nil {
-		return x.DebugInfo
-	}
-	return nil
-}
-
-func (x *Error) GetHelp() *Error_Help {
-	if x != nil {
-		return x.Help
-	}
-	return nil
-}
-
-func (x *Error) GetLocalizedMessages() []*Error_LocalizedMessage {
-	if x != nil {
-		return x.LocalizedMessages
-	}
-	return nil
-}
-
 type Filter struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -249,7 +138,7 @@ type Filter struct {
 func (x *Filter) Reset() {
 	*x = Filter{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_common_common_proto_msgTypes[1]
+		mi := &file_common_common_proto_msgTypes[0]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -262,7 +151,7 @@ func (x *Filter) String() string {
 func (*Filter) ProtoMessage() {}
 
 func (x *Filter) ProtoReflect() protoreflect.Message {
-	mi := &file_common_common_proto_msgTypes[1]
+	mi := &file_common_common_proto_msgTypes[0]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -275,7 +164,7 @@ func (x *Filter) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use Filter.ProtoReflect.Descriptor instead.
 func (*Filter) Descriptor() ([]byte, []int) {
-	return file_common_common_proto_rawDescGZIP(), []int{1}
+	return file_common_common_proto_rawDescGZIP(), []int{0}
 }
 
 func (x *Filter) GetOp() string {
@@ -314,7 +203,7 @@ type FindOptions struct {
 func (x *FindOptions) Reset() {
 	*x = FindOptions{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_common_common_proto_msgTypes[2]
+		mi := &file_common_common_proto_msgTypes[1]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -327,7 +216,7 @@ func (x *FindOptions) String() string {
 func (*FindOptions) ProtoMessage() {}
 
 func (x *FindOptions) ProtoReflect() protoreflect.Message {
-	mi := &file_common_common_proto_msgTypes[2]
+	mi := &file_common_common_proto_msgTypes[1]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -340,7 +229,7 @@ func (x *FindOptions) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use FindOptions.ProtoReflect.Descriptor instead.
 func (*FindOptions) Descriptor() ([]byte, []int) {
-	return file_common_common_proto_rawDescGZIP(), []int{2}
+	return file_common_common_proto_rawDescGZIP(), []int{1}
 }
 
 func (x *FindOptions) GetSort() []string {
@@ -396,7 +285,7 @@ type Rule struct {
 func (x *Rule) Reset() {
 	*x = Rule{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_common_common_proto_msgTypes[3]
+		mi := &file_common_common_proto_msgTypes[2]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -409,7 +298,7 @@ func (x *Rule) String() string {
 func (*Rule) ProtoMessage() {}
 
 func (x *Rule) ProtoReflect() protoreflect.Message {
-	mi := &file_common_common_proto_msgTypes[3]
+	mi := &file_common_common_proto_msgTypes[2]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -422,7 +311,7 @@ func (x *Rule) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use Rule.ProtoReflect.Descriptor instead.
 func (*Rule) Descriptor() ([]byte, []int) {
-	return file_common_common_proto_rawDescGZIP(), []int{3}
+	return file_common_common_proto_rawDescGZIP(), []int{2}
 }
 
 func (x *Rule) GetCollectionId() string {
@@ -494,7 +383,7 @@ type Collaborator struct {
 func (x *Collaborator) Reset() {
 	*x = Collaborator{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_common_common_proto_msgTypes[4]
+		mi := &file_common_common_proto_msgTypes[3]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -507,7 +396,7 @@ func (x *Collaborator) String() string {
 func (*Collaborator) ProtoMessage() {}
 
 func (x *Collaborator) ProtoReflect() protoreflect.Message {
-	mi := &file_common_common_proto_msgTypes[4]
+	mi := &file_common_common_proto_msgTypes[3]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -520,7 +409,7 @@ func (x *Collaborator) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use Collaborator.ProtoReflect.Descriptor instead.
 func (*Collaborator) Descriptor() ([]byte, []int) {
-	return file_common_common_proto_rawDescGZIP(), []int{4}
+	return file_common_common_proto_rawDescGZIP(), []int{3}
 }
 
 func (x *Collaborator) GetSpaceId() string {
@@ -559,7 +448,7 @@ type Version struct {
 func (x *Version) Reset() {
 	*x = Version{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_common_common_proto_msgTypes[5]
+		mi := &file_common_common_proto_msgTypes[4]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -572,7 +461,7 @@ func (x *Version) String() string {
 func (*Version) ProtoMessage() {}
 
 func (x *Version) ProtoReflect() protoreflect.Message {
-	mi := &file_common_common_proto_msgTypes[5]
+	mi := &file_common_common_proto_msgTypes[4]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -585,7 +474,7 @@ func (x *Version) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use Version.ProtoReflect.Descriptor instead.
 func (*Version) Descriptor() ([]byte, []int) {
-	return file_common_common_proto_rawDescGZIP(), []int{5}
+	return file_common_common_proto_rawDescGZIP(), []int{4}
 }
 
 func (x *Version) GetServerVersion() string {
@@ -623,444 +512,75 @@ func (x *Version) GetBuildNumber() int32 {
 	return 0
 }
 
-type Error_BadRequest struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Errors []*Error_BadRequest_FieldViolation `protobuf:"bytes,1,rep,name=errors,proto3" json:"errors,omitempty"`
-}
-
-func (x *Error_BadRequest) Reset() {
-	*x = Error_BadRequest{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_common_common_proto_msgTypes[6]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Error_BadRequest) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Error_BadRequest) ProtoMessage() {}
-
-func (x *Error_BadRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_common_common_proto_msgTypes[6]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use Error_BadRequest.ProtoReflect.Descriptor instead.
-func (*Error_BadRequest) Descriptor() ([]byte, []int) {
-	return file_common_common_proto_rawDescGZIP(), []int{0, 0}
-}
-
-func (x *Error_BadRequest) GetErrors() []*Error_BadRequest_FieldViolation {
-	if x != nil {
-		return x.Errors
-	}
-	return nil
-}
-
-type Error_Help struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Links []*Error_Help_Link `protobuf:"bytes,1,rep,name=links,proto3" json:"links,omitempty"`
-}
-
-func (x *Error_Help) Reset() {
-	*x = Error_Help{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_common_common_proto_msgTypes[7]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Error_Help) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Error_Help) ProtoMessage() {}
-
-func (x *Error_Help) ProtoReflect() protoreflect.Message {
-	mi := &file_common_common_proto_msgTypes[7]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use Error_Help.ProtoReflect.Descriptor instead.
-func (*Error_Help) Descriptor() ([]byte, []int) {
-	return file_common_common_proto_rawDescGZIP(), []int{0, 1}
-}
-
-func (x *Error_Help) GetLinks() []*Error_Help_Link {
-	if x != nil {
-		return x.Links
-	}
-	return nil
-}
-
-type Error_DebugInfo struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	StackTrace []string `protobuf:"bytes,1,rep,name=stack_trace,json=stackTrace,proto3" json:"stack_trace,omitempty"`
-	Detail     string   `protobuf:"bytes,2,opt,name=detail,proto3" json:"detail,omitempty"`
-}
-
-func (x *Error_DebugInfo) Reset() {
-	*x = Error_DebugInfo{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_common_common_proto_msgTypes[8]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Error_DebugInfo) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Error_DebugInfo) ProtoMessage() {}
-
-func (x *Error_DebugInfo) ProtoReflect() protoreflect.Message {
-	mi := &file_common_common_proto_msgTypes[8]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use Error_DebugInfo.ProtoReflect.Descriptor instead.
-func (*Error_DebugInfo) Descriptor() ([]byte, []int) {
-	return file_common_common_proto_rawDescGZIP(), []int{0, 2}
-}
-
-func (x *Error_DebugInfo) GetStackTrace() []string {
-	if x != nil {
-		return x.StackTrace
-	}
-	return nil
-}
-
-func (x *Error_DebugInfo) GetDetail() string {
-	if x != nil {
-		return x.Detail
-	}
-	return ""
-}
-
-type Error_LocalizedMessage struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Locale  string `protobuf:"bytes,1,opt,name=locale,proto3" json:"locale,omitempty"`
-	Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
-}
-
-func (x *Error_LocalizedMessage) Reset() {
-	*x = Error_LocalizedMessage{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_common_common_proto_msgTypes[9]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Error_LocalizedMessage) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Error_LocalizedMessage) ProtoMessage() {}
-
-func (x *Error_LocalizedMessage) ProtoReflect() protoreflect.Message {
-	mi := &file_common_common_proto_msgTypes[9]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use Error_LocalizedMessage.ProtoReflect.Descriptor instead.
-func (*Error_LocalizedMessage) Descriptor() ([]byte, []int) {
-	return file_common_common_proto_rawDescGZIP(), []int{0, 3}
-}
-
-func (x *Error_LocalizedMessage) GetLocale() string {
-	if x != nil {
-		return x.Locale
-	}
-	return ""
-}
-
-func (x *Error_LocalizedMessage) GetMessage() string {
-	if x != nil {
-		return x.Message
-	}
-	return ""
-}
-
-type Error_BadRequest_FieldViolation struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Field       string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"`
-	Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
-}
-
-func (x *Error_BadRequest_FieldViolation) Reset() {
-	*x = Error_BadRequest_FieldViolation{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_common_common_proto_msgTypes[11]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Error_BadRequest_FieldViolation) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Error_BadRequest_FieldViolation) ProtoMessage() {}
-
-func (x *Error_BadRequest_FieldViolation) ProtoReflect() protoreflect.Message {
-	mi := &file_common_common_proto_msgTypes[11]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use Error_BadRequest_FieldViolation.ProtoReflect.Descriptor instead.
-func (*Error_BadRequest_FieldViolation) Descriptor() ([]byte, []int) {
-	return file_common_common_proto_rawDescGZIP(), []int{0, 0, 0}
-}
-
-func (x *Error_BadRequest_FieldViolation) GetField() string {
-	if x != nil {
-		return x.Field
-	}
-	return ""
-}
-
-func (x *Error_BadRequest_FieldViolation) GetDescription() string {
-	if x != nil {
-		return x.Description
-	}
-	return ""
-}
-
-type Error_Help_Link struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"`
-	Url         string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
-}
-
-func (x *Error_Help_Link) Reset() {
-	*x = Error_Help_Link{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_common_common_proto_msgTypes[12]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Error_Help_Link) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Error_Help_Link) ProtoMessage() {}
-
-func (x *Error_Help_Link) ProtoReflect() protoreflect.Message {
-	mi := &file_common_common_proto_msgTypes[12]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use Error_Help_Link.ProtoReflect.Descriptor instead.
-func (*Error_Help_Link) Descriptor() ([]byte, []int) {
-	return file_common_common_proto_rawDescGZIP(), []int{0, 1, 0}
-}
-
-func (x *Error_Help_Link) GetDescription() string {
-	if x != nil {
-		return x.Description
-	}
-	return ""
-}
-
-func (x *Error_Help_Link) GetUrl() string {
-	if x != nil {
-		return x.Url
-	}
-	return ""
-}
-
 var File_common_common_proto protoreflect.FileDescriptor
 
 var file_common_common_proto_rawDesc = []byte{
 	0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
 	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x1a, 0x1c, 0x67,
 	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73,
-	0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xeb, 0x06, 0x0a, 0x05,
-	0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x63,
-	0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72,
-	0x43, 0x6f, 0x64, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x69, 0x64,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x49, 0x64, 0x12,
-	0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69,
-	0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12,
-	0x37, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x09, 0x20, 0x03, 0x28,
-	0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72,
-	0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08,
-	0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x39, 0x0a, 0x0b, 0x62, 0x61, 0x64, 0x5f,
-	0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e,
-	0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x42, 0x61, 0x64,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x62, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x0a, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x69, 0x6e, 0x66,
-	0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
-	0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f,
-	0x52, 0x09, 0x64, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x26, 0x0a, 0x04, 0x68,
-	0x65, 0x6c, 0x70, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
-	0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x70, 0x52, 0x04, 0x68,
-	0x65, 0x6c, 0x70, 0x12, 0x4e, 0x0a, 0x12, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64,
-	0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0xc8, 0x01, 0x20, 0x03, 0x28, 0x0b,
-	0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e,
-	0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
-	0x52, 0x11, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61,
-	0x67, 0x65, 0x73, 0x1a, 0x97, 0x01, 0x0a, 0x0a, 0x42, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x12, 0x3f, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03,
-	0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72, 0x6f,
-	0x72, 0x2e, 0x42, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x65,
-	0x6c, 0x64, 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x65, 0x72, 0x72,
-	0x6f, 0x72, 0x73, 0x1a, 0x48, 0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x69, 0x6f, 0x6c,
-	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64,
-	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x71, 0x0a,
-	0x04, 0x48, 0x65, 0x6c, 0x70, 0x12, 0x2d, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x01,
-	0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72,
-	0x72, 0x6f, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x70, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x05, 0x6c,
-	0x69, 0x6e, 0x6b, 0x73, 0x1a, 0x3a, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x20, 0x0a, 0x0b,
-	0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10,
-	0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c,
-	0x1a, 0x44, 0x0a, 0x09, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a,
-	0x0b, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x03,
-	0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x16,
-	0x0a, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
-	0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x1a, 0x44, 0x0a, 0x10, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69,
-	0x7a, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f,
-	0x63, 0x61, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x63, 0x61,
-	0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x3b, 0x0a, 0x0d,
-	0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
-	0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
-	0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
-	0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x5c, 0x0a, 0x06, 0x46, 0x69, 0x6c,
-	0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x02, 0x6f, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c,
-	0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
-	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65,
-	0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x98, 0x01, 0x0a, 0x0b, 0x46, 0x69, 0x6e, 0x64,
-	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18,
-	0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x70,
-	0x61, 0x67, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x70,
-	0x61, 0x67, 0x65, 0x4e, 0x75, 0x6d, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x73,
-	0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x53,
-	0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x04, 0x20,
-	0x03, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x65,
-	0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x05, 0x20,
-	0x01, 0x28, 0x08, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x46, 0x69, 0x65, 0x6c,
-	0x64, 0x73, 0x22, 0xba, 0x02, 0x0a, 0x04, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63,
-	0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64,
-	0x12, 0x28, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
-	0x0e, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f,
-	0x6e, 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x06, 0x61, 0x63,
-	0x63, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d,
-	0x6d, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65,
-	0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x5f, 0x66, 0x69, 0x65,
-	0x6c, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x68, 0x69, 0x64, 0x64, 0x65,
-	0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x61, 0x64, 0x6f,
-	0x6e, 0x6c, 0x79, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09,
-	0x52, 0x0e, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73,
-	0x12, 0x29, 0x0a, 0x10, 0x77, 0x72, 0x69, 0x74, 0x65, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x66, 0x69,
-	0x65, 0x6c, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x72, 0x69, 0x74,
-	0x65, 0x6f, 0x6e, 0x6c, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72,
-	0x65, 0x61, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x0a, 0x72, 0x65, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c,
-	0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x0b, 0x77, 0x72, 0x69, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22,
-	0x57, 0x0a, 0x0c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12,
-	0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75,
-	0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x75, 0x62,
-	0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x22, 0xab, 0x01, 0x0a, 0x07, 0x56, 0x65, 0x72,
-	0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76,
-	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65,
-	0x72, 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x61,
-	0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a,
-	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63,
-	0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, 0x6d,
-	0x6d, 0x69, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6e, 0x75, 0x6d,
-	0x62, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64,
-	0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2a, 0x25, 0x0a, 0x06, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73,
-	0x12, 0x07, 0x0a, 0x03, 0x41, 0x4e, 0x59, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x49, 0x4e,
-	0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x4f, 0x4c, 0x45, 0x10, 0x02, 0x2a, 0x43, 0x0a,
-	0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f,
-	0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x01,
-	0x12, 0x08, 0x0a, 0x04, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50,
-	0x44, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45,
-	0x10, 0x04, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x2e, 0x70, 0x65, 0x72, 0x78, 0x2e, 0x72,
-	0x75, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2d,
-	0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x3b,
-	0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5c, 0x0a, 0x06, 0x46,
+	0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x02, 0x6f, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2c, 0x0a, 0x05, 0x76,
+	0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c,
+	0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x98, 0x01, 0x0a, 0x0b, 0x46, 0x69,
+	0x6e, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6f, 0x72,
+	0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x12, 0x19, 0x0a,
+	0x08, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52,
+	0x07, 0x70, 0x61, 0x67, 0x65, 0x4e, 0x75, 0x6d, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x67, 0x65,
+	0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x61, 0x67,
+	0x65, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18,
+	0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x25, 0x0a,
+	0x0e, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18,
+	0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x46, 0x69,
+	0x65, 0x6c, 0x64, 0x73, 0x22, 0xba, 0x02, 0x0a, 0x04, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x23, 0x0a,
+	0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+	0x49, 0x64, 0x12, 0x28, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20,
+	0x03, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x74,
+	0x69, 0x6f, 0x6e, 0x52, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x0a, 0x06,
+	0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x63,
+	0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x06, 0x61, 0x63,
+	0x63, 0x65, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x5f, 0x66,
+	0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x68, 0x69, 0x64,
+	0x64, 0x65, 0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x72, 0x65, 0x61,
+	0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x06, 0x20, 0x03,
+	0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x46, 0x69, 0x65, 0x6c,
+	0x64, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x77, 0x72, 0x69, 0x74, 0x65, 0x6f, 0x6e, 0x6c, 0x79, 0x5f,
+	0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x77, 0x72,
+	0x69, 0x74, 0x65, 0x6f, 0x6e, 0x6c, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x1f, 0x0a,
+	0x0b, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x61, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x21,
+	0x0a, 0x0c, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x09,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x72, 0x69, 0x74, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65,
+	0x72, 0x22, 0x57, 0x0a, 0x0c, 0x43, 0x6f, 0x6c, 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x61, 0x74, 0x6f,
+	0x72, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07,
+	0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73,
+	0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x22, 0xab, 0x01, 0x0a, 0x07, 0x56,
+	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
+	0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d,
+	0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a,
+	0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d,
+	0x0a, 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a,
+	0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63,
+	0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6e,
+	0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x62, 0x75, 0x69,
+	0x6c, 0x64, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2a, 0x25, 0x0a, 0x06, 0x41, 0x63, 0x63, 0x65,
+	0x73, 0x73, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4e, 0x59, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4d,
+	0x49, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x4f, 0x4c, 0x45, 0x10, 0x02, 0x2a,
+	0x43, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b,
+	0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45,
+	0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06,
+	0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45,
+	0x54, 0x45, 0x10, 0x04, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x2e, 0x70, 0x65, 0x72, 0x78,
+	0x2e, 0x72, 0x75, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69,
+	0x73, 0x2d, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
+	0x6e, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -1076,41 +596,26 @@ func file_common_common_proto_rawDescGZIP() []byte {
 }
 
 var file_common_common_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
-var file_common_common_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
+var file_common_common_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
 var file_common_common_proto_goTypes = []interface{}{
-	(Access)(0),                             // 0: common.Access
-	(Action)(0),                             // 1: common.Action
-	(*Error)(nil),                           // 2: common.Error
-	(*Filter)(nil),                          // 3: common.Filter
-	(*FindOptions)(nil),                     // 4: common.FindOptions
-	(*Rule)(nil),                            // 5: common.Rule
-	(*Collaborator)(nil),                    // 6: common.Collaborator
-	(*Version)(nil),                         // 7: common.Version
-	(*Error_BadRequest)(nil),                // 8: common.Error.BadRequest
-	(*Error_Help)(nil),                      // 9: common.Error.Help
-	(*Error_DebugInfo)(nil),                 // 10: common.Error.DebugInfo
-	(*Error_LocalizedMessage)(nil),          // 11: common.Error.LocalizedMessage
-	nil,                                     // 12: common.Error.MetadataEntry
-	(*Error_BadRequest_FieldViolation)(nil), // 13: common.Error.BadRequest.FieldViolation
-	(*Error_Help_Link)(nil),                 // 14: common.Error.Help.Link
-	(*structpb.Value)(nil),                  // 15: google.protobuf.Value
+	(Access)(0),            // 0: common.Access
+	(Action)(0),            // 1: common.Action
+	(*Filter)(nil),         // 2: common.Filter
+	(*FindOptions)(nil),    // 3: common.FindOptions
+	(*Rule)(nil),           // 4: common.Rule
+	(*Collaborator)(nil),   // 5: common.Collaborator
+	(*Version)(nil),        // 6: common.Version
+	(*structpb.Value)(nil), // 7: google.protobuf.Value
 }
 var file_common_common_proto_depIdxs = []int32{
-	12, // 0: common.Error.metadata:type_name -> common.Error.MetadataEntry
-	8,  // 1: common.Error.bad_request:type_name -> common.Error.BadRequest
-	10, // 2: common.Error.debug_info:type_name -> common.Error.DebugInfo
-	9,  // 3: common.Error.help:type_name -> common.Error.Help
-	11, // 4: common.Error.localized_messages:type_name -> common.Error.LocalizedMessage
-	15, // 5: common.Filter.value:type_name -> google.protobuf.Value
-	1,  // 6: common.Rule.actions:type_name -> common.Action
-	0,  // 7: common.Rule.access:type_name -> common.Access
-	13, // 8: common.Error.BadRequest.errors:type_name -> common.Error.BadRequest.FieldViolation
-	14, // 9: common.Error.Help.links:type_name -> common.Error.Help.Link
-	10, // [10:10] is the sub-list for method output_type
-	10, // [10:10] is the sub-list for method input_type
-	10, // [10:10] is the sub-list for extension type_name
-	10, // [10:10] is the sub-list for extension extendee
-	0,  // [0:10] is the sub-list for field type_name
+	7, // 0: common.Filter.value:type_name -> google.protobuf.Value
+	1, // 1: common.Rule.actions:type_name -> common.Action
+	0, // 2: common.Rule.access:type_name -> common.Access
+	3, // [3:3] is the sub-list for method output_type
+	3, // [3:3] is the sub-list for method input_type
+	3, // [3:3] is the sub-list for extension type_name
+	3, // [3:3] is the sub-list for extension extendee
+	0, // [0:3] is the sub-list for field type_name
 }
 
 func init() { file_common_common_proto_init() }
@@ -1120,18 +625,6 @@ func file_common_common_proto_init() {
 	}
 	if !protoimpl.UnsafeEnabled {
 		file_common_common_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Error); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_common_common_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*Filter); i {
 			case 0:
 				return &v.state
@@ -1143,7 +636,7 @@ func file_common_common_proto_init() {
 				return nil
 			}
 		}
-		file_common_common_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+		file_common_common_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*FindOptions); i {
 			case 0:
 				return &v.state
@@ -1155,7 +648,7 @@ func file_common_common_proto_init() {
 				return nil
 			}
 		}
-		file_common_common_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+		file_common_common_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*Rule); i {
 			case 0:
 				return &v.state
@@ -1167,7 +660,7 @@ func file_common_common_proto_init() {
 				return nil
 			}
 		}
-		file_common_common_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+		file_common_common_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*Collaborator); i {
 			case 0:
 				return &v.state
@@ -1179,7 +672,7 @@ func file_common_common_proto_init() {
 				return nil
 			}
 		}
-		file_common_common_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+		file_common_common_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*Version); i {
 			case 0:
 				return &v.state
@@ -1191,78 +684,6 @@ func file_common_common_proto_init() {
 				return nil
 			}
 		}
-		file_common_common_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Error_BadRequest); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_common_common_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Error_Help); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_common_common_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Error_DebugInfo); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_common_common_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Error_LocalizedMessage); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_common_common_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Error_BadRequest_FieldViolation); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_common_common_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Error_Help_Link); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
 	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
@@ -1270,7 +691,7 @@ func file_common_common_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_common_common_proto_rawDesc,
 			NumEnums:      2,
-			NumMessages:   13,
+			NumMessages:   5,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/proto/common/error.pb.go b/proto/common/error.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..444bea97b4c027067c4675b3d606a3f9c85f64b8
--- /dev/null
+++ b/proto/common/error.pb.go
@@ -0,0 +1,694 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
+// source: common/error.proto
+
+package common
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	_ "google.golang.org/protobuf/types/known/structpb"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Error struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	ErrorCode         uint64                    `protobuf:"varint,1,opt,name=error_code,json=errorCode,proto3" json:"error_code,omitempty"`                                                                     // Код ошибки
+	ErrorId           string                    `protobuf:"bytes,2,opt,name=error_id,json=errorId,proto3" json:"error_id,omitempty"`                                                                            // ID конкретного инцидента, ID уникальна для каждого случая.
+	Reason            string                    `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"`                                                                                             // ???
+	Domain            string                    `protobuf:"bytes,4,opt,name=domain,proto3" json:"domain,omitempty"`                                                                                             // Сервис, пакет к которому относится ошибка
+	Message           string                    `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"`                                                                                           // Текст ошибки
+	Details           string                    `protobuf:"bytes,6,opt,name=details,proto3" json:"details,omitempty"`                                                                                           // Дополнительные детали об ошибке
+	Metadata          map[string]string         `protobuf:"bytes,9,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Мета-информация
+	BadRequest        *Error_BadRequest         `protobuf:"bytes,10,opt,name=bad_request,json=badRequest,proto3" json:"bad_request,omitempty"`                                                                  // Ошибка запроса
+	DebugInfo         *Error_DebugInfo          `protobuf:"bytes,11,opt,name=debug_info,json=debugInfo,proto3" json:"debug_info,omitempty"`                                                                     // Отладочная информация
+	Help              *Error_Help               `protobuf:"bytes,100,opt,name=help,proto3" json:"help,omitempty"`                                                                                               // Пользовательская информации и инструкции
+	LocalizedMessages []*Error_LocalizedMessage `protobuf:"bytes,200,rep,name=localized_messages,json=localizedMessages,proto3" json:"localized_messages,omitempty"`                                            // Перевод сообщения об ошибке
+	Errors            []*Error                  `protobuf:"bytes,500,rep,name=errors,proto3" json:"errors,omitempty"`                                                                                           // Вложенные ошибки
+}
+
+func (x *Error) Reset() {
+	*x = Error{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_common_error_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Error) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Error) ProtoMessage() {}
+
+func (x *Error) ProtoReflect() protoreflect.Message {
+	mi := &file_common_error_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Error.ProtoReflect.Descriptor instead.
+func (*Error) Descriptor() ([]byte, []int) {
+	return file_common_error_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Error) GetErrorCode() uint64 {
+	if x != nil {
+		return x.ErrorCode
+	}
+	return 0
+}
+
+func (x *Error) GetErrorId() string {
+	if x != nil {
+		return x.ErrorId
+	}
+	return ""
+}
+
+func (x *Error) GetReason() string {
+	if x != nil {
+		return x.Reason
+	}
+	return ""
+}
+
+func (x *Error) GetDomain() string {
+	if x != nil {
+		return x.Domain
+	}
+	return ""
+}
+
+func (x *Error) GetMessage() string {
+	if x != nil {
+		return x.Message
+	}
+	return ""
+}
+
+func (x *Error) GetDetails() string {
+	if x != nil {
+		return x.Details
+	}
+	return ""
+}
+
+func (x *Error) GetMetadata() map[string]string {
+	if x != nil {
+		return x.Metadata
+	}
+	return nil
+}
+
+func (x *Error) GetBadRequest() *Error_BadRequest {
+	if x != nil {
+		return x.BadRequest
+	}
+	return nil
+}
+
+func (x *Error) GetDebugInfo() *Error_DebugInfo {
+	if x != nil {
+		return x.DebugInfo
+	}
+	return nil
+}
+
+func (x *Error) GetHelp() *Error_Help {
+	if x != nil {
+		return x.Help
+	}
+	return nil
+}
+
+func (x *Error) GetLocalizedMessages() []*Error_LocalizedMessage {
+	if x != nil {
+		return x.LocalizedMessages
+	}
+	return nil
+}
+
+func (x *Error) GetErrors() []*Error {
+	if x != nil {
+		return x.Errors
+	}
+	return nil
+}
+
+type Error_BadRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Errors []*Error_BadRequest_FieldViolation `protobuf:"bytes,1,rep,name=errors,proto3" json:"errors,omitempty"`
+}
+
+func (x *Error_BadRequest) Reset() {
+	*x = Error_BadRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_common_error_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Error_BadRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Error_BadRequest) ProtoMessage() {}
+
+func (x *Error_BadRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_common_error_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Error_BadRequest.ProtoReflect.Descriptor instead.
+func (*Error_BadRequest) Descriptor() ([]byte, []int) {
+	return file_common_error_proto_rawDescGZIP(), []int{0, 0}
+}
+
+func (x *Error_BadRequest) GetErrors() []*Error_BadRequest_FieldViolation {
+	if x != nil {
+		return x.Errors
+	}
+	return nil
+}
+
+type Error_Help struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Links []*Error_Help_Link `protobuf:"bytes,1,rep,name=links,proto3" json:"links,omitempty"`
+}
+
+func (x *Error_Help) Reset() {
+	*x = Error_Help{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_common_error_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Error_Help) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Error_Help) ProtoMessage() {}
+
+func (x *Error_Help) ProtoReflect() protoreflect.Message {
+	mi := &file_common_error_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Error_Help.ProtoReflect.Descriptor instead.
+func (*Error_Help) Descriptor() ([]byte, []int) {
+	return file_common_error_proto_rawDescGZIP(), []int{0, 1}
+}
+
+func (x *Error_Help) GetLinks() []*Error_Help_Link {
+	if x != nil {
+		return x.Links
+	}
+	return nil
+}
+
+type Error_DebugInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	StackTrace []string `protobuf:"bytes,1,rep,name=stack_trace,json=stackTrace,proto3" json:"stack_trace,omitempty"`
+	Detail     string   `protobuf:"bytes,2,opt,name=detail,proto3" json:"detail,omitempty"`
+}
+
+func (x *Error_DebugInfo) Reset() {
+	*x = Error_DebugInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_common_error_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Error_DebugInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Error_DebugInfo) ProtoMessage() {}
+
+func (x *Error_DebugInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_common_error_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Error_DebugInfo.ProtoReflect.Descriptor instead.
+func (*Error_DebugInfo) Descriptor() ([]byte, []int) {
+	return file_common_error_proto_rawDescGZIP(), []int{0, 2}
+}
+
+func (x *Error_DebugInfo) GetStackTrace() []string {
+	if x != nil {
+		return x.StackTrace
+	}
+	return nil
+}
+
+func (x *Error_DebugInfo) GetDetail() string {
+	if x != nil {
+		return x.Detail
+	}
+	return ""
+}
+
+type Error_LocalizedMessage struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Locale  string `protobuf:"bytes,1,opt,name=locale,proto3" json:"locale,omitempty"`
+	Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
+}
+
+func (x *Error_LocalizedMessage) Reset() {
+	*x = Error_LocalizedMessage{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_common_error_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Error_LocalizedMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Error_LocalizedMessage) ProtoMessage() {}
+
+func (x *Error_LocalizedMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_common_error_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Error_LocalizedMessage.ProtoReflect.Descriptor instead.
+func (*Error_LocalizedMessage) Descriptor() ([]byte, []int) {
+	return file_common_error_proto_rawDescGZIP(), []int{0, 3}
+}
+
+func (x *Error_LocalizedMessage) GetLocale() string {
+	if x != nil {
+		return x.Locale
+	}
+	return ""
+}
+
+func (x *Error_LocalizedMessage) GetMessage() string {
+	if x != nil {
+		return x.Message
+	}
+	return ""
+}
+
+type Error_BadRequest_FieldViolation struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Field       string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"`
+	Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
+}
+
+func (x *Error_BadRequest_FieldViolation) Reset() {
+	*x = Error_BadRequest_FieldViolation{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_common_error_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Error_BadRequest_FieldViolation) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Error_BadRequest_FieldViolation) ProtoMessage() {}
+
+func (x *Error_BadRequest_FieldViolation) ProtoReflect() protoreflect.Message {
+	mi := &file_common_error_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Error_BadRequest_FieldViolation.ProtoReflect.Descriptor instead.
+func (*Error_BadRequest_FieldViolation) Descriptor() ([]byte, []int) {
+	return file_common_error_proto_rawDescGZIP(), []int{0, 0, 0}
+}
+
+func (x *Error_BadRequest_FieldViolation) GetField() string {
+	if x != nil {
+		return x.Field
+	}
+	return ""
+}
+
+func (x *Error_BadRequest_FieldViolation) GetDescription() string {
+	if x != nil {
+		return x.Description
+	}
+	return ""
+}
+
+type Error_Help_Link struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"`
+	Url         string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
+}
+
+func (x *Error_Help_Link) Reset() {
+	*x = Error_Help_Link{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_common_error_proto_msgTypes[7]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Error_Help_Link) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Error_Help_Link) ProtoMessage() {}
+
+func (x *Error_Help_Link) ProtoReflect() protoreflect.Message {
+	mi := &file_common_error_proto_msgTypes[7]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Error_Help_Link.ProtoReflect.Descriptor instead.
+func (*Error_Help_Link) Descriptor() ([]byte, []int) {
+	return file_common_error_proto_rawDescGZIP(), []int{0, 1, 0}
+}
+
+func (x *Error_Help_Link) GetDescription() string {
+	if x != nil {
+		return x.Description
+	}
+	return ""
+}
+
+func (x *Error_Help_Link) GetUrl() string {
+	if x != nil {
+		return x.Url
+	}
+	return ""
+}
+
+var File_common_error_proto protoreflect.FileDescriptor
+
+var file_common_error_proto_rawDesc = []byte{
+	0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x1a, 0x1c, 0x67, 0x6f,
+	0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74,
+	0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc7, 0x07, 0x0a, 0x05, 0x45,
+	0x72, 0x72, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f,
+	0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x43,
+	0x6f, 0x64, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x49, 0x64, 0x12, 0x16,
+	0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
+	0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x18,
+	0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61,
+	0x69, 0x6c, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69,
+	0x6c, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x09,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72,
+	0x72, 0x6f, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72,
+	0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x39, 0x0a, 0x0b, 0x62,
+	0x61, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x18, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e,
+	0x42, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x62, 0x61, 0x64, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x0a, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f,
+	0x69, 0x6e, 0x66, 0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d,
+	0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x49,
+	0x6e, 0x66, 0x6f, 0x52, 0x09, 0x64, 0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x26,
+	0x0a, 0x04, 0x68, 0x65, 0x6c, 0x70, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63,
+	0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x48, 0x65, 0x6c, 0x70,
+	0x52, 0x04, 0x68, 0x65, 0x6c, 0x70, 0x12, 0x4e, 0x0a, 0x12, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69,
+	0x7a, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0xc8, 0x01, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72,
+	0x6f, 0x72, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73,
+	0x61, 0x67, 0x65, 0x52, 0x11, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4d, 0x65,
+	0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73,
+	0x18, 0xf4, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
+	0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x1a, 0x97,
+	0x01, 0x0a, 0x0a, 0x42, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a,
+	0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e,
+	0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x42, 0x61, 0x64,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x69, 0x6f,
+	0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x1a, 0x48,
+	0x0a, 0x0e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73,
+	0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x71, 0x0a, 0x04, 0x48, 0x65, 0x6c, 0x70,
+	0x12, 0x2d, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
+	0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x48,
+	0x65, 0x6c, 0x70, 0x2e, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x1a,
+	0x3a, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
+	0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65,
+	0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x1a, 0x44, 0x0a, 0x09, 0x44,
+	0x65, 0x62, 0x75, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x63,
+	0x6b, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x73,
+	0x74, 0x61, 0x63, 0x6b, 0x54, 0x72, 0x61, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x65, 0x74,
+	0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x74, 0x61, 0x69,
+	0x6c, 0x1a, 0x44, 0x0a, 0x10, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x4d, 0x65,
+	0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x18, 0x0a,
+	0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
+	0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64,
+	0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
+	0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+	0x3a, 0x02, 0x38, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x2e, 0x70, 0x65, 0x72, 0x78,
+	0x2e, 0x72, 0x75, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69,
+	0x73, 0x2d, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
+	0x6e, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_common_error_proto_rawDescOnce sync.Once
+	file_common_error_proto_rawDescData = file_common_error_proto_rawDesc
+)
+
+func file_common_error_proto_rawDescGZIP() []byte {
+	file_common_error_proto_rawDescOnce.Do(func() {
+		file_common_error_proto_rawDescData = protoimpl.X.CompressGZIP(file_common_error_proto_rawDescData)
+	})
+	return file_common_error_proto_rawDescData
+}
+
+var file_common_error_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
+var file_common_error_proto_goTypes = []interface{}{
+	(*Error)(nil),                           // 0: common.Error
+	(*Error_BadRequest)(nil),                // 1: common.Error.BadRequest
+	(*Error_Help)(nil),                      // 2: common.Error.Help
+	(*Error_DebugInfo)(nil),                 // 3: common.Error.DebugInfo
+	(*Error_LocalizedMessage)(nil),          // 4: common.Error.LocalizedMessage
+	nil,                                     // 5: common.Error.MetadataEntry
+	(*Error_BadRequest_FieldViolation)(nil), // 6: common.Error.BadRequest.FieldViolation
+	(*Error_Help_Link)(nil),                 // 7: common.Error.Help.Link
+}
+var file_common_error_proto_depIdxs = []int32{
+	5, // 0: common.Error.metadata:type_name -> common.Error.MetadataEntry
+	1, // 1: common.Error.bad_request:type_name -> common.Error.BadRequest
+	3, // 2: common.Error.debug_info:type_name -> common.Error.DebugInfo
+	2, // 3: common.Error.help:type_name -> common.Error.Help
+	4, // 4: common.Error.localized_messages:type_name -> common.Error.LocalizedMessage
+	0, // 5: common.Error.errors:type_name -> common.Error
+	6, // 6: common.Error.BadRequest.errors:type_name -> common.Error.BadRequest.FieldViolation
+	7, // 7: common.Error.Help.links:type_name -> common.Error.Help.Link
+	8, // [8:8] is the sub-list for method output_type
+	8, // [8:8] is the sub-list for method input_type
+	8, // [8:8] is the sub-list for extension type_name
+	8, // [8:8] is the sub-list for extension extendee
+	0, // [0:8] is the sub-list for field type_name
+}
+
+func init() { file_common_error_proto_init() }
+func file_common_error_proto_init() {
+	if File_common_error_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_common_error_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Error); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_common_error_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Error_BadRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_common_error_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Error_Help); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_common_error_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Error_DebugInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_common_error_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Error_LocalizedMessage); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_common_error_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Error_BadRequest_FieldViolation); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_common_error_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Error_Help_Link); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_common_error_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   8,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_common_error_proto_goTypes,
+		DependencyIndexes: file_common_error_proto_depIdxs,
+		MessageInfos:      file_common_error_proto_msgTypes,
+	}.Build()
+	File_common_error_proto = out.File
+	file_common_error_proto_rawDesc = nil
+	file_common_error_proto_goTypes = nil
+	file_common_error_proto_depIdxs = nil
+}
diff --git a/proto/common/operation.pb.go b/proto/common/operation.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..fd84dde80f2875d3282987fb19e8df17aabed780
--- /dev/null
+++ b/proto/common/operation.pb.go
@@ -0,0 +1,288 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
+// source: common/operation.proto
+
+package common
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	anypb "google.golang.org/protobuf/types/known/anypb"
+	timestamppb "google.golang.org/protobuf/types/known/timestamppb"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// Операция
+type Operation struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// ID операции
+	Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+	// Описание операции
+	Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
+	// Время создания операции
+	CreatedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
+	// Создатель операции
+	CreatedBy string `protobuf:"bytes,4,opt,name=created_by,json=createdBy,proto3" json:"created_by,omitempty"`
+	// Время последнего изменения операции
+	ModifiedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=modified_at,json=modifiedAt,proto3" json:"modified_at,omitempty"`
+	// Операция завершена
+	Done bool `protobuf:"varint,7,opt,name=done,proto3" json:"done,omitempty"`
+	// Метаданные операции
+	Metadata *anypb.Any `protobuf:"bytes,8,opt,name=metadata,proto3" json:"metadata,omitempty"`
+	// Результат выполнения операции
+	//
+	// Types that are assignable to Result:
+	//
+	//	*Operation_Response
+	//	*Operation_Error
+	Result isOperation_Result `protobuf_oneof:"result"`
+}
+
+func (x *Operation) Reset() {
+	*x = Operation{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_common_operation_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Operation) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Operation) ProtoMessage() {}
+
+func (x *Operation) ProtoReflect() protoreflect.Message {
+	mi := &file_common_operation_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Operation.ProtoReflect.Descriptor instead.
+func (*Operation) Descriptor() ([]byte, []int) {
+	return file_common_operation_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Operation) GetId() string {
+	if x != nil {
+		return x.Id
+	}
+	return ""
+}
+
+func (x *Operation) GetDescription() string {
+	if x != nil {
+		return x.Description
+	}
+	return ""
+}
+
+func (x *Operation) GetCreatedAt() *timestamppb.Timestamp {
+	if x != nil {
+		return x.CreatedAt
+	}
+	return nil
+}
+
+func (x *Operation) GetCreatedBy() string {
+	if x != nil {
+		return x.CreatedBy
+	}
+	return ""
+}
+
+func (x *Operation) GetModifiedAt() *timestamppb.Timestamp {
+	if x != nil {
+		return x.ModifiedAt
+	}
+	return nil
+}
+
+func (x *Operation) GetDone() bool {
+	if x != nil {
+		return x.Done
+	}
+	return false
+}
+
+func (x *Operation) GetMetadata() *anypb.Any {
+	if x != nil {
+		return x.Metadata
+	}
+	return nil
+}
+
+func (m *Operation) GetResult() isOperation_Result {
+	if m != nil {
+		return m.Result
+	}
+	return nil
+}
+
+func (x *Operation) GetResponse() *anypb.Any {
+	if x, ok := x.GetResult().(*Operation_Response); ok {
+		return x.Response
+	}
+	return nil
+}
+
+func (x *Operation) GetError() *Error {
+	if x, ok := x.GetResult().(*Operation_Error); ok {
+		return x.Error
+	}
+	return nil
+}
+
+type isOperation_Result interface {
+	isOperation_Result()
+}
+
+type Operation_Response struct {
+	// Результат выполнения операции в случае успеха
+	Response *anypb.Any `protobuf:"bytes,9,opt,name=response,proto3,oneof"`
+}
+
+type Operation_Error struct {
+	// Результат выполнения операции в случае ошибки
+	Error *Error `protobuf:"bytes,10,opt,name=error,proto3,oneof"`
+}
+
+func (*Operation_Response) isOperation_Result() {}
+
+func (*Operation_Error) isOperation_Result() {}
+
+var File_common_operation_proto protoreflect.FileDescriptor
+
+var file_common_operation_proto_rawDesc = []byte{
+	0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
+	0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
+	0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+	0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+	0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x12, 0x63, 0x6f,
+	0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x22, 0xff, 0x02, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e,
+	0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x20,
+	0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
+	0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63,
+	0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x3b, 0x0a, 0x0b, 0x6d, 0x6f,
+	0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+	0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x6d, 0x6f, 0x64,
+	0x69, 0x66, 0x69, 0x65, 0x64, 0x41, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x6f, 0x6e, 0x65, 0x18,
+	0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x64, 0x6f, 0x6e, 0x65, 0x12, 0x30, 0x0a, 0x08, 0x6d,
+	0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e,
+	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+	0x41, 0x6e, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x32, 0x0a,
+	0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+	0x66, 0x2e, 0x41, 0x6e, 0x79, 0x48, 0x00, 0x52, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x12, 0x25, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x0d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48,
+	0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75,
+	0x6c, 0x74, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x2e, 0x70, 0x65, 0x72, 0x78, 0x2e, 0x72,
+	0x75, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2d,
+	0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x3b,
+	0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_common_operation_proto_rawDescOnce sync.Once
+	file_common_operation_proto_rawDescData = file_common_operation_proto_rawDesc
+)
+
+func file_common_operation_proto_rawDescGZIP() []byte {
+	file_common_operation_proto_rawDescOnce.Do(func() {
+		file_common_operation_proto_rawDescData = protoimpl.X.CompressGZIP(file_common_operation_proto_rawDescData)
+	})
+	return file_common_operation_proto_rawDescData
+}
+
+var file_common_operation_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_common_operation_proto_goTypes = []interface{}{
+	(*Operation)(nil),             // 0: common.Operation
+	(*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp
+	(*anypb.Any)(nil),             // 2: google.protobuf.Any
+	(*Error)(nil),                 // 3: common.Error
+}
+var file_common_operation_proto_depIdxs = []int32{
+	1, // 0: common.Operation.created_at:type_name -> google.protobuf.Timestamp
+	1, // 1: common.Operation.modified_at:type_name -> google.protobuf.Timestamp
+	2, // 2: common.Operation.metadata:type_name -> google.protobuf.Any
+	2, // 3: common.Operation.response:type_name -> google.protobuf.Any
+	3, // 4: common.Operation.error:type_name -> common.Error
+	5, // [5:5] is the sub-list for method output_type
+	5, // [5:5] is the sub-list for method input_type
+	5, // [5:5] is the sub-list for extension type_name
+	5, // [5:5] is the sub-list for extension extendee
+	0, // [0:5] is the sub-list for field type_name
+}
+
+func init() { file_common_operation_proto_init() }
+func file_common_operation_proto_init() {
+	if File_common_operation_proto != nil {
+		return
+	}
+	file_common_error_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_common_operation_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Operation); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	file_common_operation_proto_msgTypes[0].OneofWrappers = []interface{}{
+		(*Operation_Response)(nil),
+		(*Operation_Error)(nil),
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_common_operation_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_common_operation_proto_goTypes,
+		DependencyIndexes: file_common_operation_proto_depIdxs,
+		MessageInfos:      file_common_operation_proto_msgTypes,
+	}.Build()
+	File_common_operation_proto = out.File
+	file_common_operation_proto_rawDesc = nil
+	file_common_operation_proto_goTypes = nil
+	file_common_operation_proto_depIdxs = nil
+}
diff --git a/proto/common/operation_service.pb.go b/proto/common/operation_service.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..93876246a4f69845688755a3a88bfde6330cbcd7
--- /dev/null
+++ b/proto/common/operation_service.pb.go
@@ -0,0 +1,230 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
+// source: common/operation_service.proto
+
+package common
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type GetOperationRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	OperationId string `protobuf:"bytes,1,opt,name=operation_id,json=operationId,proto3" json:"operation_id,omitempty"`
+}
+
+func (x *GetOperationRequest) Reset() {
+	*x = GetOperationRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_common_operation_service_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetOperationRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetOperationRequest) ProtoMessage() {}
+
+func (x *GetOperationRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_common_operation_service_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetOperationRequest.ProtoReflect.Descriptor instead.
+func (*GetOperationRequest) Descriptor() ([]byte, []int) {
+	return file_common_operation_service_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *GetOperationRequest) GetOperationId() string {
+	if x != nil {
+		return x.OperationId
+	}
+	return ""
+}
+
+type CancelOperationRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	OperationId string `protobuf:"bytes,1,opt,name=operation_id,json=operationId,proto3" json:"operation_id,omitempty"`
+}
+
+func (x *CancelOperationRequest) Reset() {
+	*x = CancelOperationRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_common_operation_service_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CancelOperationRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CancelOperationRequest) ProtoMessage() {}
+
+func (x *CancelOperationRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_common_operation_service_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CancelOperationRequest.ProtoReflect.Descriptor instead.
+func (*CancelOperationRequest) Descriptor() ([]byte, []int) {
+	return file_common_operation_service_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *CancelOperationRequest) GetOperationId() string {
+	if x != nil {
+		return x.OperationId
+	}
+	return ""
+}
+
+var File_common_operation_service_proto protoreflect.FileDescriptor
+
+var file_common_operation_service_proto_rawDesc = []byte{
+	0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
+	0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x12, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x1a, 0x16, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
+	0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x1a, 0x17, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3e, 0x0a, 0x13, 0x47, 0x65, 0x74,
+	0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x12, 0x27, 0x0a, 0x0c, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x04, 0xe8, 0xc7, 0x31, 0x01, 0x52, 0x0b, 0x6f, 0x70,
+	0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x41, 0x0a, 0x16, 0x43, 0x61, 0x6e,
+	0x63, 0x65, 0x6c, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0c, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x04, 0xe8, 0xc7, 0x31, 0x01, 0x52,
+	0x0b, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x32, 0x8a, 0x01, 0x0a,
+	0x10, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
+	0x65, 0x12, 0x37, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x1b, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
+	0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4f,
+	0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x06, 0x43, 0x61,
+	0x6e, 0x63, 0x65, 0x6c, 0x12, 0x1e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x61,
+	0x6e, 0x63, 0x65, 0x6c, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4f, 0x70,
+	0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74,
+	0x2e, 0x70, 0x65, 0x72, 0x78, 0x2e, 0x72, 0x75, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2f,
+	0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2d, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
+	0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_common_operation_service_proto_rawDescOnce sync.Once
+	file_common_operation_service_proto_rawDescData = file_common_operation_service_proto_rawDesc
+)
+
+func file_common_operation_service_proto_rawDescGZIP() []byte {
+	file_common_operation_service_proto_rawDescOnce.Do(func() {
+		file_common_operation_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_common_operation_service_proto_rawDescData)
+	})
+	return file_common_operation_service_proto_rawDescData
+}
+
+var file_common_operation_service_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_common_operation_service_proto_goTypes = []interface{}{
+	(*GetOperationRequest)(nil),    // 0: common.GetOperationRequest
+	(*CancelOperationRequest)(nil), // 1: common.CancelOperationRequest
+	(*Operation)(nil),              // 2: common.Operation
+}
+var file_common_operation_service_proto_depIdxs = []int32{
+	0, // 0: common.OperationService.Get:input_type -> common.GetOperationRequest
+	1, // 1: common.OperationService.Cancel:input_type -> common.CancelOperationRequest
+	2, // 2: common.OperationService.Get:output_type -> common.Operation
+	2, // 3: common.OperationService.Cancel:output_type -> common.Operation
+	2, // [2:4] is the sub-list for method output_type
+	0, // [0:2] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_common_operation_service_proto_init() }
+func file_common_operation_service_proto_init() {
+	if File_common_operation_service_proto != nil {
+		return
+	}
+	file_common_operation_proto_init()
+	file_common_validation_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_common_operation_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetOperationRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_common_operation_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CancelOperationRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_common_operation_service_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_common_operation_service_proto_goTypes,
+		DependencyIndexes: file_common_operation_service_proto_depIdxs,
+		MessageInfos:      file_common_operation_service_proto_msgTypes,
+	}.Build()
+	File_common_operation_service_proto = out.File
+	file_common_operation_service_proto_rawDesc = nil
+	file_common_operation_service_proto_goTypes = nil
+	file_common_operation_service_proto_depIdxs = nil
+}
diff --git a/proto/common/operation_service_grpc.pb.go b/proto/common/operation_service_grpc.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..d323b0be496411363ba80de50f70246382663307
--- /dev/null
+++ b/proto/common/operation_service_grpc.pb.go
@@ -0,0 +1,150 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.3.0
+// - protoc             v4.24.3
+// source: common/operation_service.proto
+
+package common
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+const (
+	OperationService_Get_FullMethodName    = "/common.OperationService/Get"
+	OperationService_Cancel_FullMethodName = "/common.OperationService/Cancel"
+)
+
+// OperationServiceClient is the client API for OperationService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type OperationServiceClient interface {
+	// Возвращает статус операции и ее результат, если она завершена
+	Get(ctx context.Context, in *GetOperationRequest, opts ...grpc.CallOption) (*Operation, error)
+	// Отменяет выполнение операции, если это поддерживается сервисом
+	Cancel(ctx context.Context, in *CancelOperationRequest, opts ...grpc.CallOption) (*Operation, error)
+}
+
+type operationServiceClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewOperationServiceClient(cc grpc.ClientConnInterface) OperationServiceClient {
+	return &operationServiceClient{cc}
+}
+
+func (c *operationServiceClient) Get(ctx context.Context, in *GetOperationRequest, opts ...grpc.CallOption) (*Operation, error) {
+	out := new(Operation)
+	err := c.cc.Invoke(ctx, OperationService_Get_FullMethodName, in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *operationServiceClient) Cancel(ctx context.Context, in *CancelOperationRequest, opts ...grpc.CallOption) (*Operation, error) {
+	out := new(Operation)
+	err := c.cc.Invoke(ctx, OperationService_Cancel_FullMethodName, in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// OperationServiceServer is the server API for OperationService service.
+// All implementations must embed UnimplementedOperationServiceServer
+// for forward compatibility
+type OperationServiceServer interface {
+	// Возвращает статус операции и ее результат, если она завершена
+	Get(context.Context, *GetOperationRequest) (*Operation, error)
+	// Отменяет выполнение операции, если это поддерживается сервисом
+	Cancel(context.Context, *CancelOperationRequest) (*Operation, error)
+	mustEmbedUnimplementedOperationServiceServer()
+}
+
+// UnimplementedOperationServiceServer must be embedded to have forward compatible implementations.
+type UnimplementedOperationServiceServer struct {
+}
+
+func (UnimplementedOperationServiceServer) Get(context.Context, *GetOperationRequest) (*Operation, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Get not implemented")
+}
+func (UnimplementedOperationServiceServer) Cancel(context.Context, *CancelOperationRequest) (*Operation, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Cancel not implemented")
+}
+func (UnimplementedOperationServiceServer) mustEmbedUnimplementedOperationServiceServer() {}
+
+// UnsafeOperationServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to OperationServiceServer will
+// result in compilation errors.
+type UnsafeOperationServiceServer interface {
+	mustEmbedUnimplementedOperationServiceServer()
+}
+
+func RegisterOperationServiceServer(s grpc.ServiceRegistrar, srv OperationServiceServer) {
+	s.RegisterService(&OperationService_ServiceDesc, srv)
+}
+
+func _OperationService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(GetOperationRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(OperationServiceServer).Get(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: OperationService_Get_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(OperationServiceServer).Get(ctx, req.(*GetOperationRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _OperationService_Cancel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(CancelOperationRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(OperationServiceServer).Cancel(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: OperationService_Cancel_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(OperationServiceServer).Cancel(ctx, req.(*CancelOperationRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+// OperationService_ServiceDesc is the grpc.ServiceDesc for OperationService service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var OperationService_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "common.OperationService",
+	HandlerType: (*OperationServiceServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "Get",
+			Handler:    _OperationService_Get_Handler,
+		},
+		{
+			MethodName: "Cancel",
+			Handler:    _OperationService_Cancel_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "common/operation_service.proto",
+}
diff --git a/proto/common/validation.pb.go b/proto/common/validation.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..cca43ab540da1592a3bb4c21a91ba2be6f1d22bb
--- /dev/null
+++ b/proto/common/validation.pb.go
@@ -0,0 +1,315 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
+// source: common/validation.proto
+
+package common
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	descriptorpb "google.golang.org/protobuf/types/descriptorpb"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type MapKeySpec struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Value   string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
+	Pattern string `protobuf:"bytes,2,opt,name=pattern,proto3" json:"pattern,omitempty"`
+	Length  string `protobuf:"bytes,3,opt,name=length,proto3" json:"length,omitempty"`
+}
+
+func (x *MapKeySpec) Reset() {
+	*x = MapKeySpec{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_common_validation_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MapKeySpec) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MapKeySpec) ProtoMessage() {}
+
+func (x *MapKeySpec) ProtoReflect() protoreflect.Message {
+	mi := &file_common_validation_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MapKeySpec.ProtoReflect.Descriptor instead.
+func (*MapKeySpec) Descriptor() ([]byte, []int) {
+	return file_common_validation_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *MapKeySpec) GetValue() string {
+	if x != nil {
+		return x.Value
+	}
+	return ""
+}
+
+func (x *MapKeySpec) GetPattern() string {
+	if x != nil {
+		return x.Pattern
+	}
+	return ""
+}
+
+func (x *MapKeySpec) GetLength() string {
+	if x != nil {
+		return x.Length
+	}
+	return ""
+}
+
+var file_common_validation_proto_extTypes = []protoimpl.ExtensionInfo{
+	{
+		ExtendedType:  (*descriptorpb.OneofOptions)(nil),
+		ExtensionType: (*bool)(nil),
+		Field:         101400,
+		Name:          "common.exactly_one",
+		Tag:           "varint,101400,opt,name=exactly_one",
+		Filename:      "common/validation.proto",
+	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*bool)(nil),
+		Field:         101501,
+		Name:          "common.required",
+		Tag:           "varint,101501,opt,name=required",
+		Filename:      "common/validation.proto",
+	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*string)(nil),
+		Field:         101502,
+		Name:          "common.pattern",
+		Tag:           "bytes,101502,opt,name=pattern",
+		Filename:      "common/validation.proto",
+	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*string)(nil),
+		Field:         101503,
+		Name:          "common.value",
+		Tag:           "bytes,101503,opt,name=value",
+		Filename:      "common/validation.proto",
+	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*string)(nil),
+		Field:         101504,
+		Name:          "common.size",
+		Tag:           "bytes,101504,opt,name=size",
+		Filename:      "common/validation.proto",
+	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*string)(nil),
+		Field:         101505,
+		Name:          "common.length",
+		Tag:           "bytes,101505,opt,name=length",
+		Filename:      "common/validation.proto",
+	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*bool)(nil),
+		Field:         101506,
+		Name:          "common.unique",
+		Tag:           "varint,101506,opt,name=unique",
+		Filename:      "common/validation.proto",
+	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*MapKeySpec)(nil),
+		Field:         101510,
+		Name:          "common.map_key",
+		Tag:           "bytes,101510,opt,name=map_key",
+		Filename:      "common/validation.proto",
+	},
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*string)(nil),
+		Field:         101511,
+		Name:          "common.bytes",
+		Tag:           "bytes,101511,opt,name=bytes",
+		Filename:      "common/validation.proto",
+	},
+}
+
+// Extension fields to descriptorpb.OneofOptions.
+var (
+	// optional bool exactly_one = 101400;
+	E_ExactlyOne = &file_common_validation_proto_extTypes[0]
+)
+
+// Extension fields to descriptorpb.FieldOptions.
+var (
+	// optional bool required = 101501;
+	E_Required = &file_common_validation_proto_extTypes[1]
+	// optional string pattern = 101502;
+	E_Pattern = &file_common_validation_proto_extTypes[2]
+	// optional string value = 101503;
+	E_Value = &file_common_validation_proto_extTypes[3]
+	// optional string size = 101504;
+	E_Size = &file_common_validation_proto_extTypes[4]
+	// optional string length = 101505;
+	E_Length = &file_common_validation_proto_extTypes[5]
+	// optional bool unique = 101506;
+	E_Unique = &file_common_validation_proto_extTypes[6]
+	// optional common.MapKeySpec map_key = 101510;
+	E_MapKey = &file_common_validation_proto_extTypes[7]
+	// optional string bytes = 101511;
+	E_Bytes = &file_common_validation_proto_extTypes[8]
+)
+
+var File_common_validation_proto protoreflect.FileDescriptor
+
+var file_common_validation_proto_rawDesc = []byte{
+	0x0a, 0x17, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
+	0x6e, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+	0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x22, 0x54, 0x0a, 0x0a, 0x4d, 0x61, 0x70, 0x4b, 0x65, 0x79, 0x53, 0x70, 0x65,
+	0x63, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65,
+	0x72, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72,
+	0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x40, 0x0a, 0x0b, 0x65, 0x78, 0x61,
+	0x63, 0x74, 0x6c, 0x79, 0x5f, 0x6f, 0x6e, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4f, 0x6e, 0x65, 0x6f, 0x66,
+	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x98, 0x98, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x0a, 0x65, 0x78, 0x61, 0x63, 0x74, 0x6c, 0x79, 0x4f, 0x6e, 0x65, 0x3a, 0x3b, 0x0a, 0x08, 0x72,
+	0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xfd, 0x98, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08,
+	0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x3a, 0x39, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74,
+	0x65, 0x72, 0x6e, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x18, 0xfe, 0x98, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74,
+	0x65, 0x72, 0x6e, 0x3a, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, 0x2e, 0x67,
+	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46,
+	0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xff, 0x98, 0x06, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x33, 0x0a, 0x04, 0x73, 0x69,
+	0x7a, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x18, 0x80, 0x99, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x3a,
+	0x37, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c,
+	0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x81, 0x99, 0x06, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x37, 0x0a, 0x06, 0x75, 0x6e, 0x69, 0x71,
+	0x75, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x18, 0x82, 0x99, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75,
+	0x65, 0x3a, 0x4c, 0x0a, 0x07, 0x6d, 0x61, 0x70, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x1d, 0x2e, 0x67,
+	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46,
+	0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x86, 0x99, 0x06, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x61, 0x70,
+	0x4b, 0x65, 0x79, 0x53, 0x70, 0x65, 0x63, 0x52, 0x06, 0x6d, 0x61, 0x70, 0x4b, 0x65, 0x79, 0x3a,
+	0x35, 0x0a, 0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64,
+	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x87, 0x99, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x05, 0x62, 0x79, 0x74, 0x65, 0x73, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x2e, 0x70, 0x65,
+	0x72, 0x78, 0x2e, 0x72, 0x75, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2f, 0x70, 0x65, 0x72,
+	0x78, 0x69, 0x73, 0x2d, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d,
+	0x6d, 0x6f, 0x6e, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x33,
+}
+
+var (
+	file_common_validation_proto_rawDescOnce sync.Once
+	file_common_validation_proto_rawDescData = file_common_validation_proto_rawDesc
+)
+
+func file_common_validation_proto_rawDescGZIP() []byte {
+	file_common_validation_proto_rawDescOnce.Do(func() {
+		file_common_validation_proto_rawDescData = protoimpl.X.CompressGZIP(file_common_validation_proto_rawDescData)
+	})
+	return file_common_validation_proto_rawDescData
+}
+
+var file_common_validation_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_common_validation_proto_goTypes = []interface{}{
+	(*MapKeySpec)(nil),                // 0: common.MapKeySpec
+	(*descriptorpb.OneofOptions)(nil), // 1: google.protobuf.OneofOptions
+	(*descriptorpb.FieldOptions)(nil), // 2: google.protobuf.FieldOptions
+}
+var file_common_validation_proto_depIdxs = []int32{
+	1,  // 0: common.exactly_one:extendee -> google.protobuf.OneofOptions
+	2,  // 1: common.required:extendee -> google.protobuf.FieldOptions
+	2,  // 2: common.pattern:extendee -> google.protobuf.FieldOptions
+	2,  // 3: common.value:extendee -> google.protobuf.FieldOptions
+	2,  // 4: common.size:extendee -> google.protobuf.FieldOptions
+	2,  // 5: common.length:extendee -> google.protobuf.FieldOptions
+	2,  // 6: common.unique:extendee -> google.protobuf.FieldOptions
+	2,  // 7: common.map_key:extendee -> google.protobuf.FieldOptions
+	2,  // 8: common.bytes:extendee -> google.protobuf.FieldOptions
+	0,  // 9: common.map_key:type_name -> common.MapKeySpec
+	10, // [10:10] is the sub-list for method output_type
+	10, // [10:10] is the sub-list for method input_type
+	9,  // [9:10] is the sub-list for extension type_name
+	0,  // [0:9] is the sub-list for extension extendee
+	0,  // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_common_validation_proto_init() }
+func file_common_validation_proto_init() {
+	if File_common_validation_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_common_validation_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MapKeySpec); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_common_validation_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 9,
+			NumServices:   0,
+		},
+		GoTypes:           file_common_validation_proto_goTypes,
+		DependencyIndexes: file_common_validation_proto_depIdxs,
+		MessageInfos:      file_common_validation_proto_msgTypes,
+		ExtensionInfos:    file_common_validation_proto_extTypes,
+	}.Build()
+	File_common_validation_proto = out.File
+	file_common_validation_proto_rawDesc = nil
+	file_common_validation_proto_goTypes = nil
+	file_common_validation_proto_depIdxs = nil
+}
diff --git a/proto/delivery/delivery.pb.go b/proto/delivery/delivery.pb.go
index 142adddfaed2fc36900ff4e056b8df6396ec83df..a8673b3e6f8ded57f397a25ca78ae7538d3351ee 100644
--- a/proto/delivery/delivery.pb.go
+++ b/proto/delivery/delivery.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.29.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: delivery/delivery.proto
 
 package delivery
diff --git a/proto/delivery/delivery_grpc.pb.go b/proto/delivery/delivery_grpc.pb.go
index 9be07eae1c2e3938c618692fd0a7a80ba68ee432..3840ca0fc32236f38b8c92eac59c56994996be23 100644
--- a/proto/delivery/delivery_grpc.pb.go
+++ b/proto/delivery/delivery_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v3.21.12
+// - protoc             v4.24.3
 // source: delivery/delivery.proto
 
 package delivery
diff --git a/proto/environments/environments.pb.go b/proto/environments/environments.pb.go
index cec837936e68784f4edc1615f8139194fb1e7027..8dd2d620cd51e0df031c738d8409585481974062 100644
--- a/proto/environments/environments.pb.go
+++ b/proto/environments/environments.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.31.0
-// 	protoc        v4.23.4
+// 	protoc        v4.24.3
 // source: environments/environments.proto
 
 package environments
diff --git a/proto/environments/environments_grpc.pb.go b/proto/environments/environments_grpc.pb.go
index c71798565954f2f7cfc10b170fa34e31c1ae6abe..63268a0ba508b1e00b189a7588573fd12c47c0fb 100644
--- a/proto/environments/environments_grpc.pb.go
+++ b/proto/environments/environments_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v4.23.4
+// - protoc             v4.24.3
 // source: environments/environments.proto
 
 package environments
diff --git a/proto/extensions/extension.pb.go b/proto/extensions/extension.pb.go
index a518aed78f7fe6901bbffbb5d1d3760c9acf21b8..3e4f9d3dab8507a87146e783e074782217fefe71 100644
--- a/proto/extensions/extension.pb.go
+++ b/proto/extensions/extension.pb.go
@@ -10,8 +10,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.30.0
-// 	protoc        v4.23.4
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: extensions/extension.proto
 
 package extensions
@@ -96,165 +96,6 @@ func (Target) EnumDescriptor() ([]byte, []int) {
 	return file_extensions_extension_proto_rawDescGZIP(), []int{0}
 }
 
-type ExtensionRequestResult_State int32
-
-const (
-	ExtensionRequestResult_OK                  ExtensionRequestResult_State = 0 // Запрос завершен
-	ExtensionRequestResult_ERROR               ExtensionRequestResult_State = 1 // Возникла ошибка
-	ExtensionRequestResult_PENDING             ExtensionRequestResult_State = 2 // Запрос ожидает выполнения
-	ExtensionRequestResult_IN_PROGRESS         ExtensionRequestResult_State = 3 // Запрос выполняется
-	ExtensionRequestResult_PARAMETERS_REQUIRED ExtensionRequestResult_State = 4 // Требуются дополнительные данные для выполнения запроса
-)
-
-// Enum value maps for ExtensionRequestResult_State.
-var (
-	ExtensionRequestResult_State_name = map[int32]string{
-		0: "OK",
-		1: "ERROR",
-		2: "PENDING",
-		3: "IN_PROGRESS",
-		4: "PARAMETERS_REQUIRED",
-	}
-	ExtensionRequestResult_State_value = map[string]int32{
-		"OK":                  0,
-		"ERROR":               1,
-		"PENDING":             2,
-		"IN_PROGRESS":         3,
-		"PARAMETERS_REQUIRED": 4,
-	}
-)
-
-func (x ExtensionRequestResult_State) Enum() *ExtensionRequestResult_State {
-	p := new(ExtensionRequestResult_State)
-	*p = x
-	return p
-}
-
-func (x ExtensionRequestResult_State) String() string {
-	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
-}
-
-func (ExtensionRequestResult_State) Descriptor() protoreflect.EnumDescriptor {
-	return file_extensions_extension_proto_enumTypes[1].Descriptor()
-}
-
-func (ExtensionRequestResult_State) Type() protoreflect.EnumType {
-	return &file_extensions_extension_proto_enumTypes[1]
-}
-
-func (x ExtensionRequestResult_State) Number() protoreflect.EnumNumber {
-	return protoreflect.EnumNumber(x)
-}
-
-// Deprecated: Use ExtensionRequestResult_State.Descriptor instead.
-func (ExtensionRequestResult_State) EnumDescriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{1, 0}
-}
-
-type ActionResponse_State int32
-
-const (
-	ActionResponse_DONE                ActionResponse_State = 0 // Запрос завершен
-	ActionResponse_ERROR               ActionResponse_State = 1 // Возникла ошибка
-	ActionResponse_PENDING             ActionResponse_State = 2 // Запрос ожидает выполнения
-	ActionResponse_IN_PROGRESS         ActionResponse_State = 3 // Запрос выполняется
-	ActionResponse_PARAMETERS_REQUIRED ActionResponse_State = 4 // Требуются дополнительные данные для выполнения запроса
-)
-
-// Enum value maps for ActionResponse_State.
-var (
-	ActionResponse_State_name = map[int32]string{
-		0: "DONE",
-		1: "ERROR",
-		2: "PENDING",
-		3: "IN_PROGRESS",
-		4: "PARAMETERS_REQUIRED",
-	}
-	ActionResponse_State_value = map[string]int32{
-		"DONE":                0,
-		"ERROR":               1,
-		"PENDING":             2,
-		"IN_PROGRESS":         3,
-		"PARAMETERS_REQUIRED": 4,
-	}
-)
-
-func (x ActionResponse_State) Enum() *ActionResponse_State {
-	p := new(ActionResponse_State)
-	*p = x
-	return p
-}
-
-func (x ActionResponse_State) String() string {
-	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
-}
-
-func (ActionResponse_State) Descriptor() protoreflect.EnumDescriptor {
-	return file_extensions_extension_proto_enumTypes[2].Descriptor()
-}
-
-func (ActionResponse_State) Type() protoreflect.EnumType {
-	return &file_extensions_extension_proto_enumTypes[2]
-}
-
-func (x ActionResponse_State) Number() protoreflect.EnumNumber {
-	return protoreflect.EnumNumber(x)
-}
-
-// Deprecated: Use ActionResponse_State.Descriptor instead.
-func (ActionResponse_State) EnumDescriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{10, 0}
-}
-
-type ActionResponse_Format int32
-
-const (
-	ActionResponse_PLAIN    ActionResponse_Format = 0
-	ActionResponse_HTML     ActionResponse_Format = 1
-	ActionResponse_MARKDOWN ActionResponse_Format = 2
-)
-
-// Enum value maps for ActionResponse_Format.
-var (
-	ActionResponse_Format_name = map[int32]string{
-		0: "PLAIN",
-		1: "HTML",
-		2: "MARKDOWN",
-	}
-	ActionResponse_Format_value = map[string]int32{
-		"PLAIN":    0,
-		"HTML":     1,
-		"MARKDOWN": 2,
-	}
-)
-
-func (x ActionResponse_Format) Enum() *ActionResponse_Format {
-	p := new(ActionResponse_Format)
-	*p = x
-	return p
-}
-
-func (x ActionResponse_Format) String() string {
-	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
-}
-
-func (ActionResponse_Format) Descriptor() protoreflect.EnumDescriptor {
-	return file_extensions_extension_proto_enumTypes[3].Descriptor()
-}
-
-func (ActionResponse_Format) Type() protoreflect.EnumType {
-	return &file_extensions_extension_proto_enumTypes[3]
-}
-
-func (x ActionResponse_Format) Number() protoreflect.EnumNumber {
-	return protoreflect.EnumNumber(x)
-}
-
-// Deprecated: Use ActionResponse_Format.Descriptor instead.
-func (ActionResponse_Format) EnumDescriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{10, 1}
-}
-
 // Kind описывает c какой сущность системы связано действие и что требуется передать в качестве параметров
 // Интерфейс загружает пользовательские действия и отображает их в интерфейсе в зависимости от ActionType.
 type Action_Kind int32
@@ -305,11 +146,11 @@ func (x Action_Kind) String() string {
 }
 
 func (Action_Kind) Descriptor() protoreflect.EnumDescriptor {
-	return file_extensions_extension_proto_enumTypes[4].Descriptor()
+	return file_extensions_extension_proto_enumTypes[1].Descriptor()
 }
 
 func (Action_Kind) Type() protoreflect.EnumType {
-	return &file_extensions_extension_proto_enumTypes[4]
+	return &file_extensions_extension_proto_enumTypes[1]
 }
 
 func (x Action_Kind) Number() protoreflect.EnumNumber {
@@ -318,7 +159,7 @@ func (x Action_Kind) Number() protoreflect.EnumNumber {
 
 // Deprecated: Use Action_Kind.Descriptor instead.
 func (Action_Kind) EnumDescriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{11, 0}
+	return file_extensions_extension_proto_rawDescGZIP(), []int{0, 0}
 }
 
 type Action_View int32
@@ -357,823 +198,20 @@ func (x Action_View) String() string {
 }
 
 func (Action_View) Descriptor() protoreflect.EnumDescriptor {
-	return file_extensions_extension_proto_enumTypes[5].Descriptor()
+	return file_extensions_extension_proto_enumTypes[2].Descriptor()
 }
 
 func (Action_View) Type() protoreflect.EnumType {
-	return &file_extensions_extension_proto_enumTypes[5]
-}
-
-func (x Action_View) Number() protoreflect.EnumNumber {
-	return protoreflect.EnumNumber(x)
-}
-
-// Deprecated: Use Action_View.Descriptor instead.
-func (Action_View) EnumDescriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{11, 1}
-}
-
-type InstallRequest struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Extensions []string `protobuf:"bytes,10000,rep,name=extensions,proto3" json:"extensions,omitempty"`          // Список расширений для установки
-	SpaceId    string   `protobuf:"bytes,10010,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"` // Пространство для установки расширений
-	EnvId      string   `protobuf:"bytes,10020,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`       // Идентификатор окружения для установки (по умолчанию master)
-	Force      bool     `protobuf:"varint,10100,opt,name=force,proto3" json:"force,omitempty"`                   // Игнорировать и автоматически исправлять ошибки установки
-}
-
-func (x *InstallRequest) Reset() {
-	*x = InstallRequest{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_extension_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *InstallRequest) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*InstallRequest) ProtoMessage() {}
-
-func (x *InstallRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_extension_proto_msgTypes[0]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use InstallRequest.ProtoReflect.Descriptor instead.
-func (*InstallRequest) Descriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *InstallRequest) GetExtensions() []string {
-	if x != nil {
-		return x.Extensions
-	}
-	return nil
-}
-
-func (x *InstallRequest) GetSpaceId() string {
-	if x != nil {
-		return x.SpaceId
-	}
-	return ""
-}
-
-func (x *InstallRequest) GetEnvId() string {
-	if x != nil {
-		return x.EnvId
-	}
-	return ""
-}
-
-func (x *InstallRequest) GetForce() bool {
-	if x != nil {
-		return x.Force
-	}
-	return false
-}
-
-type ExtensionRequestResult struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	State           ExtensionRequestResult_State `protobuf:"varint,10000,opt,name=state,proto3,enum=extensions.ExtensionRequestResult_State" json:"state,omitempty"` // Состояние расширение
-	Extension       string                       `protobuf:"bytes,10100,opt,name=extension,proto3" json:"extension,omitempty"`                                       // Имя расширения
-	Msg             string                       `protobuf:"bytes,10200,opt,name=msg,proto3" json:"msg,omitempty"`                                                   // Сообщение
-	Error           string                       `protobuf:"bytes,10300,opt,name=error,proto3" json:"error,omitempty"`                                               // Ошибка (state == ERROR)
-	UpdateAvailable bool                         `protobuf:"varint,10400,opt,name=update_available,json=updateAvailable,proto3" json:"update_available,omitempty"`   // Доступно обновление
-}
-
-func (x *ExtensionRequestResult) Reset() {
-	*x = ExtensionRequestResult{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_extension_proto_msgTypes[1]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *ExtensionRequestResult) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*ExtensionRequestResult) ProtoMessage() {}
-
-func (x *ExtensionRequestResult) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_extension_proto_msgTypes[1]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use ExtensionRequestResult.ProtoReflect.Descriptor instead.
-func (*ExtensionRequestResult) Descriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{1}
-}
-
-func (x *ExtensionRequestResult) GetState() ExtensionRequestResult_State {
-	if x != nil {
-		return x.State
-	}
-	return ExtensionRequestResult_OK
-}
-
-func (x *ExtensionRequestResult) GetExtension() string {
-	if x != nil {
-		return x.Extension
-	}
-	return ""
-}
-
-func (x *ExtensionRequestResult) GetMsg() string {
-	if x != nil {
-		return x.Msg
-	}
-	return ""
-}
-
-func (x *ExtensionRequestResult) GetError() string {
-	if x != nil {
-		return x.Error
-	}
-	return ""
-}
-
-func (x *ExtensionRequestResult) GetUpdateAvailable() bool {
-	if x != nil {
-		return x.UpdateAvailable
-	}
-	return false
-}
-
-type InstallResponse struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Results []*ExtensionRequestResult `protobuf:"bytes,10000,rep,name=results,proto3" json:"results,omitempty"` // Список состояний расширений после установки
-}
-
-func (x *InstallResponse) Reset() {
-	*x = InstallResponse{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_extension_proto_msgTypes[2]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *InstallResponse) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*InstallResponse) ProtoMessage() {}
-
-func (x *InstallResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_extension_proto_msgTypes[2]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use InstallResponse.ProtoReflect.Descriptor instead.
-func (*InstallResponse) Descriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{2}
-}
-
-func (x *InstallResponse) GetResults() []*ExtensionRequestResult {
-	if x != nil {
-		return x.Results
-	}
-	return nil
-}
-
-type UninstallRequest struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Extensions []string `protobuf:"bytes,10000,rep,name=extensions,proto3" json:"extensions,omitempty"`          // Список расширений для удаления
-	SpaceId    string   `protobuf:"bytes,10010,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"` // Пространство для удаления расширений
-	EnvId      string   `protobuf:"bytes,10020,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`       // Идентификатор окружения для установки (по умолчанию master)
-	Remove     bool     `protobuf:"varint,10100,opt,name=remove,proto3" json:"remove,omitempty"`                 // Удалить все коллекции и данные
-	Force      bool     `protobuf:"varint,10200,opt,name=force,proto3" json:"force,omitempty"`                   // Удалять расширения вне зависимости от возможных ошибок, без учета зависимостей
-}
-
-func (x *UninstallRequest) Reset() {
-	*x = UninstallRequest{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_extension_proto_msgTypes[3]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *UninstallRequest) String() string {
-	return protoimpl.X.MessageStringOf(x)
+	return &file_extensions_extension_proto_enumTypes[2]
 }
-
-func (*UninstallRequest) ProtoMessage() {}
-
-func (x *UninstallRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_extension_proto_msgTypes[3]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use UninstallRequest.ProtoReflect.Descriptor instead.
-func (*UninstallRequest) Descriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{3}
-}
-
-func (x *UninstallRequest) GetExtensions() []string {
-	if x != nil {
-		return x.Extensions
-	}
-	return nil
-}
-
-func (x *UninstallRequest) GetSpaceId() string {
-	if x != nil {
-		return x.SpaceId
-	}
-	return ""
-}
-
-func (x *UninstallRequest) GetEnvId() string {
-	if x != nil {
-		return x.EnvId
-	}
-	return ""
-}
-
-func (x *UninstallRequest) GetRemove() bool {
-	if x != nil {
-		return x.Remove
-	}
-	return false
-}
-
-func (x *UninstallRequest) GetForce() bool {
-	if x != nil {
-		return x.Force
-	}
-	return false
-}
-
-type UninstallResponse struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Results []*ExtensionRequestResult `protobuf:"bytes,10000,rep,name=results,proto3" json:"results,omitempty"` // Список состояний расширений после удаления
-}
-
-func (x *UninstallResponse) Reset() {
-	*x = UninstallResponse{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_extension_proto_msgTypes[4]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *UninstallResponse) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*UninstallResponse) ProtoMessage() {}
-
-func (x *UninstallResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_extension_proto_msgTypes[4]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use UninstallResponse.ProtoReflect.Descriptor instead.
-func (*UninstallResponse) Descriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{4}
-}
-
-func (x *UninstallResponse) GetResults() []*ExtensionRequestResult {
-	if x != nil {
-		return x.Results
-	}
-	return nil
-}
-
-type UpdateRequest struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Extensions []string `protobuf:"bytes,10000,rep,name=extensions,proto3" json:"extensions,omitempty"`          // Список расширений для удаления
-	SpaceId    string   `protobuf:"bytes,10010,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"` // Пространство для удаления расширений
-	EnvId      string   `protobuf:"bytes,10020,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`       // Идентификатор окружения для установки (по умолчанию master)
-	Force      bool     `protobuf:"varint,10100,opt,name=force,proto3" json:"force,omitempty"`                   // Выполнять обновление и автоматически исправлять ошибки
-}
-
-func (x *UpdateRequest) Reset() {
-	*x = UpdateRequest{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_extension_proto_msgTypes[5]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *UpdateRequest) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*UpdateRequest) ProtoMessage() {}
-
-func (x *UpdateRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_extension_proto_msgTypes[5]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use UpdateRequest.ProtoReflect.Descriptor instead.
-func (*UpdateRequest) Descriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{5}
-}
-
-func (x *UpdateRequest) GetExtensions() []string {
-	if x != nil {
-		return x.Extensions
-	}
-	return nil
-}
-
-func (x *UpdateRequest) GetSpaceId() string {
-	if x != nil {
-		return x.SpaceId
-	}
-	return ""
-}
-
-func (x *UpdateRequest) GetEnvId() string {
-	if x != nil {
-		return x.EnvId
-	}
-	return ""
-}
-
-func (x *UpdateRequest) GetForce() bool {
-	if x != nil {
-		return x.Force
-	}
-	return false
-}
-
-type UpdateResponse struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Results []*ExtensionRequestResult `protobuf:"bytes,10000,rep,name=results,proto3" json:"results,omitempty"` // Список состояний расширений после удаления
-}
-
-func (x *UpdateResponse) Reset() {
-	*x = UpdateResponse{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_extension_proto_msgTypes[6]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *UpdateResponse) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*UpdateResponse) ProtoMessage() {}
-
-func (x *UpdateResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_extension_proto_msgTypes[6]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use UpdateResponse.ProtoReflect.Descriptor instead.
-func (*UpdateResponse) Descriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{6}
-}
-
-func (x *UpdateResponse) GetResults() []*ExtensionRequestResult {
-	if x != nil {
-		return x.Results
-	}
-	return nil
-}
-
-type CheckRequest struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Extensions []string `protobuf:"bytes,10000,rep,name=extensions,proto3" json:"extensions,omitempty"`          // Список расширений для удаления
-	SpaceId    string   `protobuf:"bytes,10010,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"` // Пространство для удаления расширений
-	EnvId      string   `protobuf:"bytes,10020,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`       // Идентификатор окружения для установки (по умолчанию master)
-}
-
-func (x *CheckRequest) Reset() {
-	*x = CheckRequest{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_extension_proto_msgTypes[7]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *CheckRequest) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*CheckRequest) ProtoMessage() {}
-
-func (x *CheckRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_extension_proto_msgTypes[7]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use CheckRequest.ProtoReflect.Descriptor instead.
-func (*CheckRequest) Descriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{7}
-}
-
-func (x *CheckRequest) GetExtensions() []string {
-	if x != nil {
-		return x.Extensions
-	}
-	return nil
-}
-
-func (x *CheckRequest) GetSpaceId() string {
-	if x != nil {
-		return x.SpaceId
-	}
-	return ""
-}
-
-func (x *CheckRequest) GetEnvId() string {
-	if x != nil {
-		return x.EnvId
-	}
-	return ""
-}
-
-type CheckResponse struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Results []*ExtensionRequestResult `protobuf:"bytes,10000,rep,name=results,proto3" json:"results,omitempty"` // Список состояний расширений
-}
-
-func (x *CheckResponse) Reset() {
-	*x = CheckResponse{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_extension_proto_msgTypes[8]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *CheckResponse) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*CheckResponse) ProtoMessage() {}
-
-func (x *CheckResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_extension_proto_msgTypes[8]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use CheckResponse.ProtoReflect.Descriptor instead.
-func (*CheckResponse) Descriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{8}
-}
-
-func (x *CheckResponse) GetResults() []*ExtensionRequestResult {
-	if x != nil {
-		return x.Results
-	}
-	return nil
-}
-
-// ActionRequest - запрос на выполнение действия к расширению (или менеджеру расширений)
-type ActionRequest struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Extension    string   `protobuf:"bytes,1000,opt,name=extension,proto3" json:"extension,omitempty"`
-	Action       string   `protobuf:"bytes,10100,opt,name=action,proto3" json:"action,omitempty"`
-	SpaceId      string   `protobuf:"bytes,10500,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"`
-	EnvId        string   `protobuf:"bytes,10510,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`
-	CollectionId string   `protobuf:"bytes,10520,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"`
-	ItemId       string   `protobuf:"bytes,10530,opt,name=item_id,json=itemId,proto3" json:"item_id,omitempty"`
-	ItemIds      []string `protobuf:"bytes,10540,rep,name=item_ids,json=itemIds,proto3" json:"item_ids,omitempty"`
-	// Поля к которым применимо действие. В случае если действие выполняется из списка записей, содержит перечень
-	// полей которые пользователь выбрал для отображения в интерфейсе.
-	Fields   []string          `protobuf:"bytes,10550,rep,name=fields,proto3" json:"fields,omitempty"`
-	Metadata map[string]string `protobuf:"bytes,11000,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
-	// Ссылки на записи используемые для выполнения действия (назначение ссылок зависит от действия и расширения)
-	Refs []*references.Reference `protobuf:"bytes,11010,rep,name=refs,proto3" json:"refs,omitempty"`
-	// Ссылка на документ с параметрами выполнения Action.
-	// Чтобы при выполнении действия открывалась форма параметров, необходимо указать `Action.params_collection`
-	Params *references.Reference `protobuf:"bytes,11020,opt,name=params,proto3" json:"params,omitempty"`
-}
-
-func (x *ActionRequest) Reset() {
-	*x = ActionRequest{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_extension_proto_msgTypes[9]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *ActionRequest) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*ActionRequest) ProtoMessage() {}
-
-func (x *ActionRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_extension_proto_msgTypes[9]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use ActionRequest.ProtoReflect.Descriptor instead.
-func (*ActionRequest) Descriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{9}
-}
-
-func (x *ActionRequest) GetExtension() string {
-	if x != nil {
-		return x.Extension
-	}
-	return ""
-}
-
-func (x *ActionRequest) GetAction() string {
-	if x != nil {
-		return x.Action
-	}
-	return ""
-}
-
-func (x *ActionRequest) GetSpaceId() string {
-	if x != nil {
-		return x.SpaceId
-	}
-	return ""
-}
-
-func (x *ActionRequest) GetEnvId() string {
-	if x != nil {
-		return x.EnvId
-	}
-	return ""
-}
-
-func (x *ActionRequest) GetCollectionId() string {
-	if x != nil {
-		return x.CollectionId
-	}
-	return ""
-}
-
-func (x *ActionRequest) GetItemId() string {
-	if x != nil {
-		return x.ItemId
-	}
-	return ""
-}
-
-func (x *ActionRequest) GetItemIds() []string {
-	if x != nil {
-		return x.ItemIds
-	}
-	return nil
-}
-
-func (x *ActionRequest) GetFields() []string {
-	if x != nil {
-		return x.Fields
-	}
-	return nil
-}
-
-func (x *ActionRequest) GetMetadata() map[string]string {
-	if x != nil {
-		return x.Metadata
-	}
-	return nil
-}
-
-func (x *ActionRequest) GetRefs() []*references.Reference {
-	if x != nil {
-		return x.Refs
-	}
-	return nil
-}
-
-func (x *ActionRequest) GetParams() *references.Reference {
-	if x != nil {
-		return x.Params
-	}
-	return nil
-}
-
-type ActionResponse struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	State    ActionResponse_State    `protobuf:"varint,10000,opt,name=state,proto3,enum=extensions.ActionResponse_State" json:"state,omitempty"`                                                         // Состояние расширение
-	Target   Target                  `protobuf:"varint,10010,opt,name=target,proto3,enum=extensions.Target" json:"target,omitempty"`                                                                     // Как открывать результат выполнения действия в пользовательском интерфейсе (переопределяет значение в Action)
-	Format   ActionResponse_Format   `protobuf:"varint,10050,opt,name=format,proto3,enum=extensions.ActionResponse_Format" json:"format,omitempty"`                                                      // Формат полей msg  и error
-	Msg      string                  `protobuf:"bytes,10100,opt,name=msg,proto3" json:"msg,omitempty"`                                                                                                   // Сообщение о выполнении действия
-	Title    string                  `protobuf:"bytes,10110,opt,name=title,proto3" json:"title,omitempty"`                                                                                               // Текст для отображения в интерфейсе
-	Image    string                  `protobuf:"bytes,10140,opt,name=image,proto3" json:"image,omitempty"`                                                                                               // Изображение для отображения в интерфейсе (шапке окна)
-	Error    string                  `protobuf:"bytes,10200,opt,name=error,proto3" json:"error,omitempty"`                                                                                               // Сообщение в случае ошибки (дополнительно к msg)
-	Next     []*Action               `protobuf:"bytes,10300,rep,name=next,proto3" json:"next,omitempty"`                                                                                                 // Следующие возможные действия. Интерфейс отображает как варианты дальнейших действий пользователя
-	Metadata map[string]string       `protobuf:"bytes,10400,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Метаданные запроса
-	Refs     []*references.Reference `protobuf:"bytes,10320,rep,name=refs,proto3" json:"refs,omitempty"`                                                                                                 // Ссылки на записи (назначение ссылок зависит от действия и расширения)
-}
-
-func (x *ActionResponse) Reset() {
-	*x = ActionResponse{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_extension_proto_msgTypes[10]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *ActionResponse) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*ActionResponse) ProtoMessage() {}
-
-func (x *ActionResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_extension_proto_msgTypes[10]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use ActionResponse.ProtoReflect.Descriptor instead.
-func (*ActionResponse) Descriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{10}
-}
-
-func (x *ActionResponse) GetState() ActionResponse_State {
-	if x != nil {
-		return x.State
-	}
-	return ActionResponse_DONE
-}
-
-func (x *ActionResponse) GetTarget() Target {
-	if x != nil {
-		return x.Target
-	}
-	return Target_DEFAULT
-}
-
-func (x *ActionResponse) GetFormat() ActionResponse_Format {
-	if x != nil {
-		return x.Format
-	}
-	return ActionResponse_PLAIN
-}
-
-func (x *ActionResponse) GetMsg() string {
-	if x != nil {
-		return x.Msg
-	}
-	return ""
-}
-
-func (x *ActionResponse) GetTitle() string {
-	if x != nil {
-		return x.Title
-	}
-	return ""
-}
-
-func (x *ActionResponse) GetImage() string {
-	if x != nil {
-		return x.Image
-	}
-	return ""
-}
-
-func (x *ActionResponse) GetError() string {
-	if x != nil {
-		return x.Error
-	}
-	return ""
-}
-
-func (x *ActionResponse) GetNext() []*Action {
-	if x != nil {
-		return x.Next
-	}
-	return nil
-}
-
-func (x *ActionResponse) GetMetadata() map[string]string {
-	if x != nil {
-		return x.Metadata
-	}
-	return nil
+
+func (x Action_View) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
 }
 
-func (x *ActionResponse) GetRefs() []*references.Reference {
-	if x != nil {
-		return x.Refs
-	}
-	return nil
+// Deprecated: Use Action_View.Descriptor instead.
+func (Action_View) EnumDescriptor() ([]byte, []int) {
+	return file_extensions_extension_proto_rawDescGZIP(), []int{0, 1}
 }
 
 // *
@@ -1242,7 +280,7 @@ type Action struct {
 	// форма создания записи в указанной коллекции
 	ParamsCollection string         `protobuf:"bytes,10330,opt,name=params_collection,json=paramsCollection,proto3" json:"params_collection,omitempty"`
 	Request          *ActionRequest `protobuf:"bytes,10400,opt,name=request,proto3" json:"request,omitempty"` // Параметры запроса (используется в случае `ActionResponse.next`)
-	// DEPRECATED: Используйте `action_url` вместо `navigation_route`
+	// DEPRECATED: Используйте `action` вместо `navigation_route`
 	NavigationAction bool `protobuf:"varint,10500,opt,name=navigation_action,json=navigationAction,proto3" json:"navigation_action,omitempty"` // Флаг указывающий что действие переносить пользователя в другую часть интерфейса, а не отправляет запрос на сервер
 	// navigation_route - Строка шаблон для перехода в интерфейсе
 	// При указании полного адреса (http(s)://xyz), URL открывается в новом окне браузера
@@ -1255,7 +293,7 @@ type Action struct {
 	// - :colId
 	// - :itemId
 	//
-	// DEPRECATED: Используйте `action_type == "NAVIGATION"`
+	// DEPRECATED: Используйте `action`
 	NavigationRoute string `protobuf:"bytes,10510,opt,name=navigation_route,json=navigationRoute,proto3" json:"navigation_route,omitempty"`
 	// Параметр указывающий что действие выполняется автоматически
 	// Если указано, то действие выполняется автоматически каждый раз при загрузке приложения
@@ -1270,7 +308,7 @@ type Action struct {
 func (x *Action) Reset() {
 	*x = Action{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_extension_proto_msgTypes[11]
+		mi := &file_extensions_extension_proto_msgTypes[0]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1283,7 +321,7 @@ func (x *Action) String() string {
 func (*Action) ProtoMessage() {}
 
 func (x *Action) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_extension_proto_msgTypes[11]
+	mi := &file_extensions_extension_proto_msgTypes[0]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1296,7 +334,7 @@ func (x *Action) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use Action.ProtoReflect.Descriptor instead.
 func (*Action) Descriptor() ([]byte, []int) {
-	return file_extensions_extension_proto_rawDescGZIP(), []int{11}
+	return file_extensions_extension_proto_rawDescGZIP(), []int{0}
 }
 
 func (x *Action) GetExtension() string {
@@ -1439,6 +477,139 @@ func (x *Action) GetOrder() int32 {
 	return 0
 }
 
+// ActionRequest - запрос на выполнение действия к расширению (или менеджеру расширений)
+type ActionRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Extension    string   `protobuf:"bytes,1000,opt,name=extension,proto3" json:"extension,omitempty"`
+	Action       string   `protobuf:"bytes,10100,opt,name=action,proto3" json:"action,omitempty"`
+	SpaceId      string   `protobuf:"bytes,10500,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"`
+	EnvId        string   `protobuf:"bytes,10510,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`
+	CollectionId string   `protobuf:"bytes,10520,opt,name=collection_id,json=collectionId,proto3" json:"collection_id,omitempty"`
+	ItemId       string   `protobuf:"bytes,10530,opt,name=item_id,json=itemId,proto3" json:"item_id,omitempty"`
+	ItemIds      []string `protobuf:"bytes,10540,rep,name=item_ids,json=itemIds,proto3" json:"item_ids,omitempty"`
+	// Поля к которым применимо действие. В случае если действие выполняется из списка записей, содержит перечень
+	// полей которые пользователь выбрал для отображения в интерфейсе.
+	Fields   []string          `protobuf:"bytes,10550,rep,name=fields,proto3" json:"fields,omitempty"`
+	Metadata map[string]string `protobuf:"bytes,11000,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+	// Ссылки на записи используемые для выполнения действия (назначение ссылок зависит от действия и расширения)
+	Refs []*references.Reference `protobuf:"bytes,11010,rep,name=refs,proto3" json:"refs,omitempty"`
+	// Ссылка на документ с параметрами выполнения Action.
+	// Чтобы при выполнении действия открывалась форма параметров, необходимо указать `Action.params_collection`
+	Params *references.Reference `protobuf:"bytes,11020,opt,name=params,proto3" json:"params,omitempty"`
+}
+
+func (x *ActionRequest) Reset() {
+	*x = ActionRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_extension_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ActionRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ActionRequest) ProtoMessage() {}
+
+func (x *ActionRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_extension_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ActionRequest.ProtoReflect.Descriptor instead.
+func (*ActionRequest) Descriptor() ([]byte, []int) {
+	return file_extensions_extension_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *ActionRequest) GetExtension() string {
+	if x != nil {
+		return x.Extension
+	}
+	return ""
+}
+
+func (x *ActionRequest) GetAction() string {
+	if x != nil {
+		return x.Action
+	}
+	return ""
+}
+
+func (x *ActionRequest) GetSpaceId() string {
+	if x != nil {
+		return x.SpaceId
+	}
+	return ""
+}
+
+func (x *ActionRequest) GetEnvId() string {
+	if x != nil {
+		return x.EnvId
+	}
+	return ""
+}
+
+func (x *ActionRequest) GetCollectionId() string {
+	if x != nil {
+		return x.CollectionId
+	}
+	return ""
+}
+
+func (x *ActionRequest) GetItemId() string {
+	if x != nil {
+		return x.ItemId
+	}
+	return ""
+}
+
+func (x *ActionRequest) GetItemIds() []string {
+	if x != nil {
+		return x.ItemIds
+	}
+	return nil
+}
+
+func (x *ActionRequest) GetFields() []string {
+	if x != nil {
+		return x.Fields
+	}
+	return nil
+}
+
+func (x *ActionRequest) GetMetadata() map[string]string {
+	if x != nil {
+		return x.Metadata
+	}
+	return nil
+}
+
+func (x *ActionRequest) GetRefs() []*references.Reference {
+	if x != nil {
+		return x.Refs
+	}
+	return nil
+}
+
+func (x *ActionRequest) GetParams() *references.Reference {
+	if x != nil {
+		return x.Params
+	}
+	return nil
+}
+
 var File_extensions_extension_proto protoreflect.FileDescriptor
 
 var file_extensions_extension_proto_rawDesc = []byte{
@@ -1446,77 +617,65 @@ var file_extensions_extension_proto_rawDesc = []byte{
 	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x65, 0x78,
 	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x1b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
 	0x6e, 0x63, 0x65, 0x73, 0x2f, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7c, 0x0a, 0x0e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x90, 0x4e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78,
-	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63,
-	0x65, 0x5f, 0x69, 0x64, 0x18, 0x9a, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61,
-	0x63, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0xa4,
-	0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x05,
-	0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0xf4, 0x4e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f,
-	0x72, 0x63, 0x65, 0x22, 0xa1, 0x02, 0x0a, 0x16, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
-	0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x3f,
-	0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x90, 0x4e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28,
-	0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x65,
-	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75,
-	0x6c, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12,
-	0x1d, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0xf4, 0x4e, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x11,
-	0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0xd8, 0x4f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73,
-	0x67, 0x12, 0x15, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xbc, 0x50, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2a, 0x0a, 0x10, 0x75, 0x70, 0x64, 0x61,
-	0x74, 0x65, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0xa0, 0x51, 0x20,
-	0x01, 0x28, 0x08, 0x52, 0x0f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x76, 0x61, 0x69, 0x6c,
-	0x61, 0x62, 0x6c, 0x65, 0x22, 0x51, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x06, 0x0a,
-	0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x01,
-	0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0f, 0x0a,
-	0x0b, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x03, 0x12, 0x17,
-	0x0a, 0x13, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45, 0x54, 0x45, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51,
-	0x55, 0x49, 0x52, 0x45, 0x44, 0x10, 0x04, 0x22, 0x50, 0x0a, 0x0f, 0x49, 0x6e, 0x73, 0x74, 0x61,
-	0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x07, 0x72, 0x65,
-	0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x90, 0x4e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65,
-	0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73,
-	0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74,
-	0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x10, 0x55, 0x6e,
-	0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f,
-	0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x90, 0x4e, 0x20,
-	0x03, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12,
-	0x1a, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x9a, 0x4e, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x65,
-	0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0xa4, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e,
-	0x76, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x18, 0xf4, 0x4e,
-	0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x15, 0x0a, 0x05,
-	0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0xd8, 0x4f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f,
-	0x72, 0x63, 0x65, 0x22, 0x52, 0x0a, 0x11, 0x55, 0x6e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c,
-	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75,
-	0x6c, 0x74, 0x73, 0x18, 0x90, 0x4e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x78, 0x74,
-	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
-	0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07,
-	0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x7b, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74,
-	0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65,
-	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x90, 0x4e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x65,
-	0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x70, 0x61,
-	0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x9a, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70,
-	0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18,
-	0xa4, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x15, 0x0a,
-	0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0xf4, 0x4e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66,
-	0x6f, 0x72, 0x63, 0x65, 0x22, 0x4f, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74,
-	0x73, 0x18, 0x90, 0x4e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65,
-	0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x63, 0x0a, 0x0c, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
-	0x6f, 0x6e, 0x73, 0x18, 0x90, 0x4e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65,
-	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f,
-	0x69, 0x64, 0x18, 0x9a, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65,
-	0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0xa4, 0x4e, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x22, 0x4e, 0x0a, 0x0d, 0x43, 0x68,
-	0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x07, 0x72,
-	0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x90, 0x4e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e,
-	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c,
-	0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0xdf, 0x03, 0x0a, 0x0d, 0x41,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa2, 0x07, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+	0x12, 0x1d, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x90, 0x4e,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12,
+	0x17, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xf4, 0x4e, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67,
+	0x65, 0x74, 0x18, 0xfe, 0x4e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x65, 0x78, 0x74, 0x65,
+	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x06, 0x74,
+	0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x17, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18,
+	0x88, 0x4f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x13,
+	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0xd8, 0x4f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x18, 0xe2, 0x4f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
+	0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x13, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0xec,
+	0x4f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x05, 0x69,
+	0x6d, 0x61, 0x67, 0x65, 0x18, 0xf6, 0x4f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f,
+	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73,
+	0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67,
+	0x65, 0x12, 0x17, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x80, 0x50, 0x20, 0x03,
+	0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x2c, 0x0a, 0x04, 0x6b, 0x69,
+	0x6e, 0x64, 0x18, 0xbc, 0x50, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x65, 0x78, 0x74, 0x65,
+	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4b, 0x69,
+	0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x19, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73,
+	0x73, 0x65, 0x73, 0x18, 0xc6, 0x50, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73,
+	0x73, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x04, 0x72, 0x65, 0x66, 0x73, 0x18, 0xd0, 0x50, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x72, 0x65, 0x66,
+	0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
+	0x65, 0x52, 0x04, 0x72, 0x65, 0x66, 0x73, 0x12, 0x2c, 0x0a, 0x11, 0x70, 0x61, 0x72, 0x61, 0x6d,
+	0x73, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xda, 0x50, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x65,
+	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x18, 0xa0, 0x51, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
+	0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x6e,
+	0x61, 0x76, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+	0x18, 0x84, 0x52, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x6e, 0x61, 0x76, 0x69, 0x67, 0x61, 0x74,
+	0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x10, 0x6e, 0x61, 0x76,
+	0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x18, 0x8e, 0x52,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6e, 0x61, 0x76, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x07, 0x61, 0x75, 0x74, 0x6f, 0x72, 0x75, 0x6e,
+	0x18, 0x98, 0x52, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x61, 0x75, 0x74, 0x6f, 0x72, 0x75, 0x6e,
+	0x12, 0x19, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x18, 0xa2, 0x52, 0x20, 0x01,
+	0x28, 0x08, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x12, 0x2c, 0x0a, 0x04, 0x76,
+	0x69, 0x65, 0x77, 0x18, 0xac, 0x52, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x65, 0x78, 0x74,
+	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x56,
+	0x69, 0x65, 0x77, 0x52, 0x04, 0x76, 0x69, 0x65, 0x77, 0x12, 0x15, 0x0a, 0x05, 0x6f, 0x72, 0x64,
+	0x65, 0x72, 0x18, 0xb6, 0x52, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72,
+	0x22, 0x6e, 0x0a, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41,
+	0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x01,
+	0x12, 0x0f, 0x0a, 0x0b, 0x45, 0x4e, 0x56, 0x49, 0x52, 0x4f, 0x4e, 0x4d, 0x45, 0x4e, 0x54, 0x10,
+	0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x4f, 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10,
+	0x03, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x54, 0x45, 0x4d, 0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x49,
+	0x54, 0x45, 0x4d, 0x53, 0x10, 0x05, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x45, 0x56, 0x49, 0x53, 0x49,
+	0x4f, 0x4e, 0x10, 0x06, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x07,
+	0x22, 0x58, 0x0a, 0x04, 0x56, 0x69, 0x65, 0x77, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x45, 0x46, 0x41,
+	0x55, 0x4c, 0x54, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x48, 0x49,
+	0x44, 0x44, 0x45, 0x4e, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x4d,
+	0x41, 0x49, 0x4e, 0x5f, 0x4d, 0x45, 0x4e, 0x55, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, 0x02, 0x12,
+	0x19, 0x0a, 0x15, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x4d, 0x45, 0x4e, 0x55, 0x5f, 0x42, 0x4f, 0x54,
+	0x54, 0x4f, 0x4d, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, 0x03, 0x22, 0xdf, 0x03, 0x0a, 0x0d, 0x41,
 	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x09,
 	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0xe8, 0x07, 0x20, 0x01, 0x28, 0x09,
 	0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x06, 0x61,
@@ -1546,138 +705,18 @@ var file_extensions_extension_proto_rawDesc = []byte{
 	0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79,
 	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
 	0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xed, 0x04, 0x0a,
-	0x0e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
-	0x37, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x90, 0x4e, 0x20, 0x01, 0x28, 0x0e, 0x32,
-	0x20, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74,
-	0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74,
-	0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67,
-	0x65, 0x74, 0x18, 0x9a, 0x4e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x65, 0x78, 0x74, 0x65,
-	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x06, 0x74,
-	0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x3a, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18,
-	0xc2, 0x4e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
-	0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61,
-	0x74, 0x12, 0x11, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0xf4, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x03, 0x6d, 0x73, 0x67, 0x12, 0x15, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0xfe, 0x4e,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x15, 0x0a, 0x05, 0x69,
-	0x6d, 0x61, 0x67, 0x65, 0x18, 0x9c, 0x4f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61,
-	0x67, 0x65, 0x12, 0x15, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xd8, 0x4f, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x27, 0x0a, 0x04, 0x6e, 0x65, 0x78,
-	0x74, 0x18, 0xbc, 0x50, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x6e, 0x65,
-	0x78, 0x74, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0xa0,
-	0x51, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
-	0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
-	0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x32, 0x0a, 0x04, 0x72, 0x65, 0x66,
-	0x73, 0x18, 0xd0, 0x50, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
-	0x6e, 0x74, 0x2e, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65,
-	0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x04, 0x72, 0x65, 0x66, 0x73, 0x1a, 0x3b, 0x0a,
-	0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
-	0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
-	0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x53, 0x0a, 0x05, 0x53, 0x74,
-	0x61, 0x74, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a,
-	0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44,
-	0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47,
-	0x52, 0x45, 0x53, 0x53, 0x10, 0x03, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x41, 0x52, 0x41, 0x4d, 0x45,
-	0x54, 0x45, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x10, 0x04, 0x22,
-	0x2b, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41,
-	0x49, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x4d, 0x4c, 0x10, 0x01, 0x12, 0x0c,
-	0x0a, 0x08, 0x4d, 0x41, 0x52, 0x4b, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x02, 0x22, 0xa2, 0x07, 0x0a,
-	0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x18, 0x90, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x74,
-	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
-	0x18, 0xf4, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
-	0x2b, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0xfe, 0x4e, 0x20, 0x01, 0x28, 0x0e,
-	0x32, 0x12, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x54, 0x61,
-	0x72, 0x67, 0x65, 0x74, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x17, 0x0a, 0x06,
-	0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x88, 0x4f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70,
-	0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x13, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0xd8, 0x4f,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x64, 0x65,
-	0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe2, 0x4f, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x13, 0x0a,
-	0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0xec, 0x4f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63,
-	0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0xf6, 0x4f, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x72, 0x65, 0x66,
-	0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
-	0x65, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x17, 0x0a, 0x06, 0x67, 0x72, 0x6f, 0x75,
-	0x70, 0x73, 0x18, 0x80, 0x50, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x67, 0x72, 0x6f, 0x75, 0x70,
-	0x73, 0x12, 0x2c, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0xbc, 0x50, 0x20, 0x01, 0x28, 0x0e,
-	0x32, 0x17, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63,
-	0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4b, 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12,
-	0x19, 0x0a, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0xc6, 0x50, 0x20, 0x03, 0x28,
-	0x09, 0x52, 0x07, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x04, 0x72, 0x65,
-	0x66, 0x73, 0x18, 0xd0, 0x50, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
-	0x65, 0x6e, 0x74, 0x2e, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x52,
-	0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x04, 0x72, 0x65, 0x66, 0x73, 0x12, 0x2c,
-	0x0a, 0x11, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
-	0x69, 0x6f, 0x6e, 0x18, 0xda, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x61, 0x72, 0x61,
-	0x6d, 0x73, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x0a, 0x07,
-	0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0xa0, 0x51, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19,
-	0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69,
-	0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x6e, 0x61, 0x76, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-	0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x84, 0x52, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10,
-	0x6e, 0x61, 0x76, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e,
-	0x12, 0x2a, 0x0a, 0x10, 0x6e, 0x61, 0x76, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72,
-	0x6f, 0x75, 0x74, 0x65, 0x18, 0x8e, 0x52, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6e, 0x61, 0x76,
-	0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x07,
-	0x61, 0x75, 0x74, 0x6f, 0x72, 0x75, 0x6e, 0x18, 0x98, 0x52, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
-	0x61, 0x75, 0x74, 0x6f, 0x72, 0x75, 0x6e, 0x12, 0x19, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69,
-	0x72, 0x6d, 0x18, 0xa2, 0x52, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x66, 0x69,
-	0x72, 0x6d, 0x12, 0x2c, 0x0a, 0x04, 0x76, 0x69, 0x65, 0x77, 0x18, 0xac, 0x52, 0x20, 0x01, 0x28,
-	0x0e, 0x32, 0x17, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41,
-	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x56, 0x69, 0x65, 0x77, 0x52, 0x04, 0x76, 0x69, 0x65, 0x77,
-	0x12, 0x15, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0xb6, 0x52, 0x20, 0x01, 0x28, 0x05,
-	0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x22, 0x6e, 0x0a, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x12,
-	0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05,
-	0x53, 0x50, 0x41, 0x43, 0x45, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x45, 0x4e, 0x56, 0x49, 0x52,
-	0x4f, 0x4e, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x4f, 0x4c, 0x4c,
-	0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x54, 0x45, 0x4d,
-	0x10, 0x04, 0x12, 0x09, 0x0a, 0x05, 0x49, 0x54, 0x45, 0x4d, 0x53, 0x10, 0x05, 0x12, 0x0c, 0x0a,
-	0x08, 0x52, 0x45, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x06, 0x12, 0x0a, 0x0a, 0x06, 0x43,
-	0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x07, 0x22, 0x58, 0x0a, 0x04, 0x56, 0x69, 0x65, 0x77, 0x12,
-	0x10, 0x0a, 0x0c, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x10,
-	0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x48, 0x49, 0x44, 0x44, 0x45, 0x4e, 0x5f, 0x56, 0x49, 0x45, 0x57,
-	0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x4d, 0x45, 0x4e, 0x55, 0x5f,
-	0x56, 0x49, 0x45, 0x57, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x4d, 0x41, 0x49, 0x4e, 0x5f, 0x4d,
-	0x45, 0x4e, 0x55, 0x5f, 0x42, 0x4f, 0x54, 0x54, 0x4f, 0x4d, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x10,
-	0x03, 0x2a, 0x67, 0x0a, 0x06, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x44,
-	0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x4d, 0x4f, 0x44, 0x41,
-	0x4c, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x49, 0x44, 0x45, 0x10, 0x02, 0x12, 0x08, 0x0a,
-	0x04, 0x4d, 0x41, 0x49, 0x4e, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x52, 0x41, 0x57, 0x45,
-	0x52, 0x10, 0x05, 0x12, 0x10, 0x0a, 0x0c, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54,
-	0x49, 0x4f, 0x4e, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x42, 0x4c, 0x41, 0x4e, 0x4b, 0x10, 0x07,
-	0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x64, 0x32, 0xe3, 0x02, 0x0a, 0x09, 0x45,
-	0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x07, 0x49, 0x6e, 0x73, 0x74,
-	0x61, 0x6c, 0x6c, 0x12, 0x1a, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
-	0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
-	0x1b, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x6e, 0x73,
-	0x74, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3e,
-	0x0a, 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x18, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
-	0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x1a, 0x19, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43,
-	0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41,
-	0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
-	0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
-	0x00, 0x12, 0x4a, 0x0a, 0x09, 0x55, 0x6e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x1c,
-	0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x55, 0x6e, 0x69, 0x6e,
-	0x73, 0x74, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x65,
-	0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x73, 0x74,
-	0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a,
-	0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
-	0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
-	0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
-	0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x2e, 0x70, 0x65, 0x72, 0x78, 0x2e, 0x72, 0x75, 0x2f,
-	0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2d, 0x67, 0x6f,
-	0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
-	0x73, 0x3b, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x33,
+	0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x2a, 0x67, 0x0a, 0x06,
+	0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c,
+	0x54, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x4d, 0x4f, 0x44, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x08,
+	0x0a, 0x04, 0x57, 0x49, 0x44, 0x45, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x41, 0x49, 0x4e,
+	0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x52, 0x41, 0x57, 0x45, 0x52, 0x10, 0x05, 0x12, 0x10,
+	0x0a, 0x0c, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x06,
+	0x12, 0x09, 0x0a, 0x05, 0x42, 0x4c, 0x41, 0x4e, 0x4b, 0x10, 0x07, 0x12, 0x08, 0x0a, 0x04, 0x4e,
+	0x4f, 0x4e, 0x45, 0x10, 0x64, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x2e, 0x70, 0x65, 0x72,
+	0x78, 0x2e, 0x72, 0x75, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x78,
+	0x69, 0x73, 0x2d, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x78, 0x74, 0x65,
+	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x3b, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -1692,67 +731,32 @@ func file_extensions_extension_proto_rawDescGZIP() []byte {
 	return file_extensions_extension_proto_rawDescData
 }
 
-var file_extensions_extension_proto_enumTypes = make([]protoimpl.EnumInfo, 6)
-var file_extensions_extension_proto_msgTypes = make([]protoimpl.MessageInfo, 14)
+var file_extensions_extension_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
+var file_extensions_extension_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
 var file_extensions_extension_proto_goTypes = []interface{}{
-	(Target)(0),                       // 0: extensions.Target
-	(ExtensionRequestResult_State)(0), // 1: extensions.ExtensionRequestResult.State
-	(ActionResponse_State)(0),         // 2: extensions.ActionResponse.State
-	(ActionResponse_Format)(0),        // 3: extensions.ActionResponse.Format
-	(Action_Kind)(0),                  // 4: extensions.Action.Kind
-	(Action_View)(0),                  // 5: extensions.Action.View
-	(*InstallRequest)(nil),            // 6: extensions.InstallRequest
-	(*ExtensionRequestResult)(nil),    // 7: extensions.ExtensionRequestResult
-	(*InstallResponse)(nil),           // 8: extensions.InstallResponse
-	(*UninstallRequest)(nil),          // 9: extensions.UninstallRequest
-	(*UninstallResponse)(nil),         // 10: extensions.UninstallResponse
-	(*UpdateRequest)(nil),             // 11: extensions.UpdateRequest
-	(*UpdateResponse)(nil),            // 12: extensions.UpdateResponse
-	(*CheckRequest)(nil),              // 13: extensions.CheckRequest
-	(*CheckResponse)(nil),             // 14: extensions.CheckResponse
-	(*ActionRequest)(nil),             // 15: extensions.ActionRequest
-	(*ActionResponse)(nil),            // 16: extensions.ActionResponse
-	(*Action)(nil),                    // 17: extensions.Action
-	nil,                               // 18: extensions.ActionRequest.MetadataEntry
-	nil,                               // 19: extensions.ActionResponse.MetadataEntry
-	(*references.Reference)(nil),      // 20: content.references.Reference
+	(Target)(0),                  // 0: extensions.Target
+	(Action_Kind)(0),             // 1: extensions.Action.Kind
+	(Action_View)(0),             // 2: extensions.Action.View
+	(*Action)(nil),               // 3: extensions.Action
+	(*ActionRequest)(nil),        // 4: extensions.ActionRequest
+	nil,                          // 5: extensions.ActionRequest.MetadataEntry
+	(*references.Reference)(nil), // 6: content.references.Reference
 }
 var file_extensions_extension_proto_depIdxs = []int32{
-	1,  // 0: extensions.ExtensionRequestResult.state:type_name -> extensions.ExtensionRequestResult.State
-	7,  // 1: extensions.InstallResponse.results:type_name -> extensions.ExtensionRequestResult
-	7,  // 2: extensions.UninstallResponse.results:type_name -> extensions.ExtensionRequestResult
-	7,  // 3: extensions.UpdateResponse.results:type_name -> extensions.ExtensionRequestResult
-	7,  // 4: extensions.CheckResponse.results:type_name -> extensions.ExtensionRequestResult
-	18, // 5: extensions.ActionRequest.metadata:type_name -> extensions.ActionRequest.MetadataEntry
-	20, // 6: extensions.ActionRequest.refs:type_name -> content.references.Reference
-	20, // 7: extensions.ActionRequest.params:type_name -> content.references.Reference
-	2,  // 8: extensions.ActionResponse.state:type_name -> extensions.ActionResponse.State
-	0,  // 9: extensions.ActionResponse.target:type_name -> extensions.Target
-	3,  // 10: extensions.ActionResponse.format:type_name -> extensions.ActionResponse.Format
-	17, // 11: extensions.ActionResponse.next:type_name -> extensions.Action
-	19, // 12: extensions.ActionResponse.metadata:type_name -> extensions.ActionResponse.MetadataEntry
-	20, // 13: extensions.ActionResponse.refs:type_name -> content.references.Reference
-	0,  // 14: extensions.Action.target:type_name -> extensions.Target
-	20, // 15: extensions.Action.image:type_name -> content.references.Reference
-	4,  // 16: extensions.Action.kind:type_name -> extensions.Action.Kind
-	20, // 17: extensions.Action.refs:type_name -> content.references.Reference
-	15, // 18: extensions.Action.request:type_name -> extensions.ActionRequest
-	5,  // 19: extensions.Action.view:type_name -> extensions.Action.View
-	6,  // 20: extensions.Extension.Install:input_type -> extensions.InstallRequest
-	13, // 21: extensions.Extension.Check:input_type -> extensions.CheckRequest
-	11, // 22: extensions.Extension.Update:input_type -> extensions.UpdateRequest
-	9,  // 23: extensions.Extension.Uninstall:input_type -> extensions.UninstallRequest
-	15, // 24: extensions.Extension.Action:input_type -> extensions.ActionRequest
-	8,  // 25: extensions.Extension.Install:output_type -> extensions.InstallResponse
-	14, // 26: extensions.Extension.Check:output_type -> extensions.CheckResponse
-	12, // 27: extensions.Extension.Update:output_type -> extensions.UpdateResponse
-	10, // 28: extensions.Extension.Uninstall:output_type -> extensions.UninstallResponse
-	16, // 29: extensions.Extension.Action:output_type -> extensions.ActionResponse
-	25, // [25:30] is the sub-list for method output_type
-	20, // [20:25] is the sub-list for method input_type
-	20, // [20:20] is the sub-list for extension type_name
-	20, // [20:20] is the sub-list for extension extendee
-	0,  // [0:20] is the sub-list for field type_name
+	0, // 0: extensions.Action.target:type_name -> extensions.Target
+	6, // 1: extensions.Action.image:type_name -> content.references.Reference
+	1, // 2: extensions.Action.kind:type_name -> extensions.Action.Kind
+	6, // 3: extensions.Action.refs:type_name -> content.references.Reference
+	4, // 4: extensions.Action.request:type_name -> extensions.ActionRequest
+	2, // 5: extensions.Action.view:type_name -> extensions.Action.View
+	5, // 6: extensions.ActionRequest.metadata:type_name -> extensions.ActionRequest.MetadataEntry
+	6, // 7: extensions.ActionRequest.refs:type_name -> content.references.Reference
+	6, // 8: extensions.ActionRequest.params:type_name -> content.references.Reference
+	9, // [9:9] is the sub-list for method output_type
+	9, // [9:9] is the sub-list for method input_type
+	9, // [9:9] is the sub-list for extension type_name
+	9, // [9:9] is the sub-list for extension extendee
+	0, // [0:9] is the sub-list for field type_name
 }
 
 func init() { file_extensions_extension_proto_init() }
@@ -1762,7 +766,7 @@ func file_extensions_extension_proto_init() {
 	}
 	if !protoimpl.UnsafeEnabled {
 		file_extensions_extension_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*InstallRequest); i {
+			switch v := v.(*Action); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1774,102 +778,6 @@ func file_extensions_extension_proto_init() {
 			}
 		}
 		file_extensions_extension_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ExtensionRequestResult); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_extension_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*InstallResponse); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_extension_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*UninstallRequest); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_extension_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*UninstallResponse); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_extension_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*UpdateRequest); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_extension_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*UpdateResponse); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_extension_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CheckRequest); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_extension_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CheckResponse); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_extension_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*ActionRequest); i {
 			case 0:
 				return &v.state
@@ -1881,40 +789,16 @@ func file_extensions_extension_proto_init() {
 				return nil
 			}
 		}
-		file_extensions_extension_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ActionResponse); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_extension_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Action); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
 	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_extensions_extension_proto_rawDesc,
-			NumEnums:      6,
-			NumMessages:   14,
+			NumEnums:      3,
+			NumMessages:   3,
 			NumExtensions: 0,
-			NumServices:   1,
+			NumServices:   0,
 		},
 		GoTypes:           file_extensions_extension_proto_goTypes,
 		DependencyIndexes: file_extensions_extension_proto_depIdxs,
diff --git a/proto/extensions/extension_service.pb.go b/proto/extensions/extension_service.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..ef0d0a82baa9f2683c12b24d8ca85e917904d655
--- /dev/null
+++ b/proto/extensions/extension_service.pb.go
@@ -0,0 +1,696 @@
+//*
+//# Расширения (Extensions)
+//
+//Расширения представляют собой отдельные сервисы предоставляющие дополнительные возможности для пользователей. Сервис
+//может предоставлять несколько расширений одновременно.
+//
+//Для координации взаимодействия используется сервис менеджер расширений (Extension Manager). Для предоставления своих функций в систему сервис должен
+//зарегистрироваться на контроллере.
+//
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
+// source: extensions/extension_service.proto
+
+package extensions
+
+import (
+	common "git.perx.ru/perxis/perxis-go/proto/common"
+	references "git.perx.ru/perxis/perxis-go/proto/references"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type ActionResponse_State int32
+
+const (
+	ActionResponse_DONE                ActionResponse_State = 0 // Запрос завершен
+	ActionResponse_ERROR               ActionResponse_State = 1 // Возникла ошибка
+	ActionResponse_PENDING             ActionResponse_State = 2 // Запрос ожидает выполнения
+	ActionResponse_IN_PROGRESS         ActionResponse_State = 3 // Запрос выполняется
+	ActionResponse_PARAMETERS_REQUIRED ActionResponse_State = 4 // Требуются дополнительные данные для выполнения запроса
+)
+
+// Enum value maps for ActionResponse_State.
+var (
+	ActionResponse_State_name = map[int32]string{
+		0: "DONE",
+		1: "ERROR",
+		2: "PENDING",
+		3: "IN_PROGRESS",
+		4: "PARAMETERS_REQUIRED",
+	}
+	ActionResponse_State_value = map[string]int32{
+		"DONE":                0,
+		"ERROR":               1,
+		"PENDING":             2,
+		"IN_PROGRESS":         3,
+		"PARAMETERS_REQUIRED": 4,
+	}
+)
+
+func (x ActionResponse_State) Enum() *ActionResponse_State {
+	p := new(ActionResponse_State)
+	*p = x
+	return p
+}
+
+func (x ActionResponse_State) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (ActionResponse_State) Descriptor() protoreflect.EnumDescriptor {
+	return file_extensions_extension_service_proto_enumTypes[0].Descriptor()
+}
+
+func (ActionResponse_State) Type() protoreflect.EnumType {
+	return &file_extensions_extension_service_proto_enumTypes[0]
+}
+
+func (x ActionResponse_State) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use ActionResponse_State.Descriptor instead.
+func (ActionResponse_State) EnumDescriptor() ([]byte, []int) {
+	return file_extensions_extension_service_proto_rawDescGZIP(), []int{3, 0}
+}
+
+type ActionResponse_Format int32
+
+const (
+	ActionResponse_PLAIN    ActionResponse_Format = 0
+	ActionResponse_HTML     ActionResponse_Format = 1
+	ActionResponse_MARKDOWN ActionResponse_Format = 2
+)
+
+// Enum value maps for ActionResponse_Format.
+var (
+	ActionResponse_Format_name = map[int32]string{
+		0: "PLAIN",
+		1: "HTML",
+		2: "MARKDOWN",
+	}
+	ActionResponse_Format_value = map[string]int32{
+		"PLAIN":    0,
+		"HTML":     1,
+		"MARKDOWN": 2,
+	}
+)
+
+func (x ActionResponse_Format) Enum() *ActionResponse_Format {
+	p := new(ActionResponse_Format)
+	*p = x
+	return p
+}
+
+func (x ActionResponse_Format) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (ActionResponse_Format) Descriptor() protoreflect.EnumDescriptor {
+	return file_extensions_extension_service_proto_enumTypes[1].Descriptor()
+}
+
+func (ActionResponse_Format) Type() protoreflect.EnumType {
+	return &file_extensions_extension_service_proto_enumTypes[1]
+}
+
+func (x ActionResponse_Format) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use ActionResponse_Format.Descriptor instead.
+func (ActionResponse_Format) EnumDescriptor() ([]byte, []int) {
+	return file_extensions_extension_service_proto_rawDescGZIP(), []int{3, 1}
+}
+
+// InstallRequest - запрос на установку расширений
+type InstallRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Extensions []string `protobuf:"bytes,10000,rep,name=extensions,proto3" json:"extensions,omitempty"`          // Список расширений для установки
+	SpaceId    string   `protobuf:"bytes,10010,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"` // Пространство для установки расширений
+	EnvId      string   `protobuf:"bytes,10020,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`       // Идентификатор окружения для установки
+	Force      bool     `protobuf:"varint,10100,opt,name=force,proto3" json:"force,omitempty"`                   // Устанавливать расширения вне зависимости от возможных ошибок
+}
+
+func (x *InstallRequest) Reset() {
+	*x = InstallRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_extension_service_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *InstallRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*InstallRequest) ProtoMessage() {}
+
+func (x *InstallRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_extension_service_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use InstallRequest.ProtoReflect.Descriptor instead.
+func (*InstallRequest) Descriptor() ([]byte, []int) {
+	return file_extensions_extension_service_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *InstallRequest) GetExtensions() []string {
+	if x != nil {
+		return x.Extensions
+	}
+	return nil
+}
+
+func (x *InstallRequest) GetSpaceId() string {
+	if x != nil {
+		return x.SpaceId
+	}
+	return ""
+}
+
+func (x *InstallRequest) GetEnvId() string {
+	if x != nil {
+		return x.EnvId
+	}
+	return ""
+}
+
+func (x *InstallRequest) GetForce() bool {
+	if x != nil {
+		return x.Force
+	}
+	return false
+}
+
+// UninstallRequest - запрос на удаление расширений
+type UninstallRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Extensions []string `protobuf:"bytes,10000,rep,name=extensions,proto3" json:"extensions,omitempty"`          // Список расширений для удаления
+	SpaceId    string   `protobuf:"bytes,10010,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"` // Пространство для удаления расширений
+	EnvId      string   `protobuf:"bytes,10020,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`       // Идентификатор окружения для установки (по умолчанию master)
+	Remove     bool     `protobuf:"varint,10100,opt,name=remove,proto3" json:"remove,omitempty"`                 // Удалить сделанные расширением изменения в пространстве, если возможно
+	Force      bool     `protobuf:"varint,10200,opt,name=force,proto3" json:"force,omitempty"`                   // Удалять расширения вне зависимости от возможных ошибок, без учета зависимостей
+}
+
+func (x *UninstallRequest) Reset() {
+	*x = UninstallRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_extension_service_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *UninstallRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UninstallRequest) ProtoMessage() {}
+
+func (x *UninstallRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_extension_service_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use UninstallRequest.ProtoReflect.Descriptor instead.
+func (*UninstallRequest) Descriptor() ([]byte, []int) {
+	return file_extensions_extension_service_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *UninstallRequest) GetExtensions() []string {
+	if x != nil {
+		return x.Extensions
+	}
+	return nil
+}
+
+func (x *UninstallRequest) GetSpaceId() string {
+	if x != nil {
+		return x.SpaceId
+	}
+	return ""
+}
+
+func (x *UninstallRequest) GetEnvId() string {
+	if x != nil {
+		return x.EnvId
+	}
+	return ""
+}
+
+func (x *UninstallRequest) GetRemove() bool {
+	if x != nil {
+		return x.Remove
+	}
+	return false
+}
+
+func (x *UninstallRequest) GetForce() bool {
+	if x != nil {
+		return x.Force
+	}
+	return false
+}
+
+// CheckRequest - запрос на проверку статуса установки расширений
+type CheckRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Extensions []string `protobuf:"bytes,10000,rep,name=extensions,proto3" json:"extensions,omitempty"`          // Список расширений для проверки
+	SpaceId    string   `protobuf:"bytes,10010,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"` // Пространство
+	EnvId      string   `protobuf:"bytes,10020,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`       // Идентификатор окружения (по умолчанию master)
+}
+
+func (x *CheckRequest) Reset() {
+	*x = CheckRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_extension_service_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CheckRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CheckRequest) ProtoMessage() {}
+
+func (x *CheckRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_extension_service_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CheckRequest.ProtoReflect.Descriptor instead.
+func (*CheckRequest) Descriptor() ([]byte, []int) {
+	return file_extensions_extension_service_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *CheckRequest) GetExtensions() []string {
+	if x != nil {
+		return x.Extensions
+	}
+	return nil
+}
+
+func (x *CheckRequest) GetSpaceId() string {
+	if x != nil {
+		return x.SpaceId
+	}
+	return ""
+}
+
+func (x *CheckRequest) GetEnvId() string {
+	if x != nil {
+		return x.EnvId
+	}
+	return ""
+}
+
+type ActionResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	State    ActionResponse_State    `protobuf:"varint,10000,opt,name=state,proto3,enum=extensions.ActionResponse_State" json:"state,omitempty"`                                                         // Состояние расширение
+	Target   Target                  `protobuf:"varint,10010,opt,name=target,proto3,enum=extensions.Target" json:"target,omitempty"`                                                                     // Как открывать результат выполнения действия в пользовательском интерфейсе (переопределяет значение в Action)
+	Format   ActionResponse_Format   `protobuf:"varint,10050,opt,name=format,proto3,enum=extensions.ActionResponse_Format" json:"format,omitempty"`                                                      // Формат полей msg  и error
+	Msg      string                  `protobuf:"bytes,10100,opt,name=msg,proto3" json:"msg,omitempty"`                                                                                                   // Сообщение о выполнении действия
+	Title    string                  `protobuf:"bytes,10110,opt,name=title,proto3" json:"title,omitempty"`                                                                                               // Текст для отображения в интерфейсе
+	Image    string                  `protobuf:"bytes,10140,opt,name=image,proto3" json:"image,omitempty"`                                                                                               // Изображение для отображения в интерфейсе (шапке окна)
+	Error    string                  `protobuf:"bytes,10200,opt,name=error,proto3" json:"error,omitempty"`                                                                                               // Сообщение в случае ошибки (дополнительно к msg)
+	Next     []*Action               `protobuf:"bytes,10300,rep,name=next,proto3" json:"next,omitempty"`                                                                                                 // Следующие возможные действия. Интерфейс отображает как варианты дальнейших действий пользователя
+	Metadata map[string]string       `protobuf:"bytes,10400,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Метаданные запроса
+	Refs     []*references.Reference `protobuf:"bytes,10320,rep,name=refs,proto3" json:"refs,omitempty"`                                                                                                 // Ссылки на записи (назначение ссылок зависит от действия и расширения)
+}
+
+func (x *ActionResponse) Reset() {
+	*x = ActionResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_extension_service_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ActionResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ActionResponse) ProtoMessage() {}
+
+func (x *ActionResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_extension_service_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ActionResponse.ProtoReflect.Descriptor instead.
+func (*ActionResponse) Descriptor() ([]byte, []int) {
+	return file_extensions_extension_service_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *ActionResponse) GetState() ActionResponse_State {
+	if x != nil {
+		return x.State
+	}
+	return ActionResponse_DONE
+}
+
+func (x *ActionResponse) GetTarget() Target {
+	if x != nil {
+		return x.Target
+	}
+	return Target_DEFAULT
+}
+
+func (x *ActionResponse) GetFormat() ActionResponse_Format {
+	if x != nil {
+		return x.Format
+	}
+	return ActionResponse_PLAIN
+}
+
+func (x *ActionResponse) GetMsg() string {
+	if x != nil {
+		return x.Msg
+	}
+	return ""
+}
+
+func (x *ActionResponse) GetTitle() string {
+	if x != nil {
+		return x.Title
+	}
+	return ""
+}
+
+func (x *ActionResponse) GetImage() string {
+	if x != nil {
+		return x.Image
+	}
+	return ""
+}
+
+func (x *ActionResponse) GetError() string {
+	if x != nil {
+		return x.Error
+	}
+	return ""
+}
+
+func (x *ActionResponse) GetNext() []*Action {
+	if x != nil {
+		return x.Next
+	}
+	return nil
+}
+
+func (x *ActionResponse) GetMetadata() map[string]string {
+	if x != nil {
+		return x.Metadata
+	}
+	return nil
+}
+
+func (x *ActionResponse) GetRefs() []*references.Reference {
+	if x != nil {
+		return x.Refs
+	}
+	return nil
+}
+
+var File_extensions_extension_service_proto protoreflect.FileDescriptor
+
+var file_extensions_extension_service_proto_rawDesc = []byte{
+	0x0a, 0x22, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x65, 0x78, 0x74,
+	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
+	0x1a, 0x1b, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x72, 0x65, 0x66,
+	0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x16, 0x63,
+	0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x73, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x22, 0x7c, 0x0a, 0x0e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x73, 0x18, 0x90, 0x4e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
+	0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64,
+	0x18, 0x9a, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64,
+	0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0xa4, 0x4e, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63,
+	0x65, 0x18, 0xf4, 0x4e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22,
+	0x97, 0x01, 0x0a, 0x10, 0x55, 0x6e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
+	0x6e, 0x73, 0x18, 0x90, 0x4e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e,
+	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69,
+	0x64, 0x18, 0x9a, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49,
+	0x64, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0xa4, 0x4e, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x06, 0x72, 0x65, 0x6d,
+	0x6f, 0x76, 0x65, 0x18, 0xf4, 0x4e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x6d, 0x6f,
+	0x76, 0x65, 0x12, 0x15, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0xd8, 0x4f, 0x20, 0x01,
+	0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x63, 0x0a, 0x0c, 0x43, 0x68, 0x65,
+	0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0a, 0x65, 0x78, 0x74,
+	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x90, 0x4e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a,
+	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x70,
+	0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x9a, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73,
+	0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64,
+	0x18, 0xa4, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x22, 0xed,
+	0x04, 0x0a, 0x0e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x12, 0x37, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x90, 0x4e, 0x20, 0x01, 0x28,
+	0x0e, 0x32, 0x20, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41,
+	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x74,
+	0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x74, 0x61,
+	0x72, 0x67, 0x65, 0x74, 0x18, 0x9a, 0x4e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x65, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52,
+	0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x3a, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61,
+	0x74, 0x18, 0xc2, 0x4e, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e,
+	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72,
+	0x6d, 0x61, 0x74, 0x12, 0x11, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0xf4, 0x4e, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x15, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18,
+	0xfe, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x15, 0x0a,
+	0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x9c, 0x4f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69,
+	0x6d, 0x61, 0x67, 0x65, 0x12, 0x15, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xd8, 0x4f,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x27, 0x0a, 0x04, 0x6e,
+	0x65, 0x78, 0x74, 0x18, 0xbc, 0x50, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x65, 0x78, 0x74,
+	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04,
+	0x6e, 0x65, 0x78, 0x74, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+	0x18, 0xa0, 0x51, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
+	0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72,
+	0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x32, 0x0a, 0x04, 0x72,
+	0x65, 0x66, 0x73, 0x18, 0xd0, 0x50, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e,
+	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x2e,
+	0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x04, 0x72, 0x65, 0x66, 0x73, 0x1a,
+	0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79,
+	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
+	0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x53, 0x0a, 0x05,
+	0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12,
+	0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45,
+	0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x5f, 0x50, 0x52,
+	0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x03, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x41, 0x52, 0x41,
+	0x4d, 0x45, 0x54, 0x45, 0x52, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x10,
+	0x04, 0x22, 0x2b, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x09, 0x0a, 0x05, 0x50,
+	0x4c, 0x41, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x4d, 0x4c, 0x10, 0x01,
+	0x12, 0x0c, 0x0a, 0x08, 0x4d, 0x41, 0x52, 0x4b, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x02, 0x32, 0x89,
+	0x02, 0x0a, 0x10, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76,
+	0x69, 0x63, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x1a,
+	0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x6e, 0x73, 0x74,
+	0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x63, 0x6f, 0x6d,
+	0x6d, 0x6f, 0x6e, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x12,
+	0x3e, 0x0a, 0x09, 0x55, 0x6e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x12, 0x1c, 0x2e, 0x65,
+	0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x73, 0x74,
+	0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x63, 0x6f, 0x6d,
+	0x6d, 0x6f, 0x6e, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x12,
+	0x36, 0x0a, 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x18, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e,
+	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x1a, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4f, 0x70, 0x65, 0x72,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f,
+	0x6e, 0x12, 0x19, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41,
+	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x65,
+	0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69,
+	0x74, 0x2e, 0x70, 0x65, 0x72, 0x78, 0x2e, 0x72, 0x75, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73,
+	0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2d, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x3b, 0x65, 0x78, 0x74, 0x65,
+	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_extensions_extension_service_proto_rawDescOnce sync.Once
+	file_extensions_extension_service_proto_rawDescData = file_extensions_extension_service_proto_rawDesc
+)
+
+func file_extensions_extension_service_proto_rawDescGZIP() []byte {
+	file_extensions_extension_service_proto_rawDescOnce.Do(func() {
+		file_extensions_extension_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_extensions_extension_service_proto_rawDescData)
+	})
+	return file_extensions_extension_service_proto_rawDescData
+}
+
+var file_extensions_extension_service_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
+var file_extensions_extension_service_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+var file_extensions_extension_service_proto_goTypes = []interface{}{
+	(ActionResponse_State)(0),    // 0: extensions.ActionResponse.State
+	(ActionResponse_Format)(0),   // 1: extensions.ActionResponse.Format
+	(*InstallRequest)(nil),       // 2: extensions.InstallRequest
+	(*UninstallRequest)(nil),     // 3: extensions.UninstallRequest
+	(*CheckRequest)(nil),         // 4: extensions.CheckRequest
+	(*ActionResponse)(nil),       // 5: extensions.ActionResponse
+	nil,                          // 6: extensions.ActionResponse.MetadataEntry
+	(Target)(0),                  // 7: extensions.Target
+	(*Action)(nil),               // 8: extensions.Action
+	(*references.Reference)(nil), // 9: content.references.Reference
+	(*ActionRequest)(nil),        // 10: extensions.ActionRequest
+	(*common.Operation)(nil),     // 11: common.Operation
+}
+var file_extensions_extension_service_proto_depIdxs = []int32{
+	0,  // 0: extensions.ActionResponse.state:type_name -> extensions.ActionResponse.State
+	7,  // 1: extensions.ActionResponse.target:type_name -> extensions.Target
+	1,  // 2: extensions.ActionResponse.format:type_name -> extensions.ActionResponse.Format
+	8,  // 3: extensions.ActionResponse.next:type_name -> extensions.Action
+	6,  // 4: extensions.ActionResponse.metadata:type_name -> extensions.ActionResponse.MetadataEntry
+	9,  // 5: extensions.ActionResponse.refs:type_name -> content.references.Reference
+	2,  // 6: extensions.ExtensionService.Install:input_type -> extensions.InstallRequest
+	3,  // 7: extensions.ExtensionService.Uninstall:input_type -> extensions.UninstallRequest
+	4,  // 8: extensions.ExtensionService.Check:input_type -> extensions.CheckRequest
+	10, // 9: extensions.ExtensionService.Action:input_type -> extensions.ActionRequest
+	11, // 10: extensions.ExtensionService.Install:output_type -> common.Operation
+	11, // 11: extensions.ExtensionService.Uninstall:output_type -> common.Operation
+	11, // 12: extensions.ExtensionService.Check:output_type -> common.Operation
+	5,  // 13: extensions.ExtensionService.Action:output_type -> extensions.ActionResponse
+	10, // [10:14] is the sub-list for method output_type
+	6,  // [6:10] is the sub-list for method input_type
+	6,  // [6:6] is the sub-list for extension type_name
+	6,  // [6:6] is the sub-list for extension extendee
+	0,  // [0:6] is the sub-list for field type_name
+}
+
+func init() { file_extensions_extension_service_proto_init() }
+func file_extensions_extension_service_proto_init() {
+	if File_extensions_extension_service_proto != nil {
+		return
+	}
+	file_extensions_extension_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_extensions_extension_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*InstallRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_extension_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*UninstallRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_extension_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CheckRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_extension_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ActionResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_extensions_extension_service_proto_rawDesc,
+			NumEnums:      2,
+			NumMessages:   5,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_extensions_extension_service_proto_goTypes,
+		DependencyIndexes: file_extensions_extension_service_proto_depIdxs,
+		EnumInfos:         file_extensions_extension_service_proto_enumTypes,
+		MessageInfos:      file_extensions_extension_service_proto_msgTypes,
+	}.Build()
+	File_extensions_extension_service_proto = out.File
+	file_extensions_extension_service_proto_rawDesc = nil
+	file_extensions_extension_service_proto_goTypes = nil
+	file_extensions_extension_service_proto_depIdxs = nil
+}
diff --git a/proto/extensions/extension_grpc.pb.go b/proto/extensions/extension_service_grpc.pb.go
similarity index 50%
rename from proto/extensions/extension_grpc.pb.go
rename to proto/extensions/extension_service_grpc.pb.go
index 784527d37db21be2337aa3406ef3dd83eb87e940..3cd37b839f83963d40818ddb7f094894c56f2f12 100644
--- a/proto/extensions/extension_grpc.pb.go
+++ b/proto/extensions/extension_service_grpc.pb.go
@@ -11,13 +11,14 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v4.23.4
-// source: extensions/extension.proto
+// - protoc             v4.24.3
+// source: extensions/extension_service.proto
 
 package extensions
 
 import (
 	context "context"
+	common "git.perx.ru/perxis/perxis-go/proto/common"
 	grpc "google.golang.org/grpc"
 	codes "google.golang.org/grpc/codes"
 	status "google.golang.org/grpc/status"
@@ -29,29 +30,25 @@ import (
 const _ = grpc.SupportPackageIsVersion7
 
 const (
-	Extension_Install_FullMethodName   = "/extensions.Extension/Install"
-	Extension_Check_FullMethodName     = "/extensions.Extension/Check"
-	Extension_Update_FullMethodName    = "/extensions.Extension/Update"
-	Extension_Uninstall_FullMethodName = "/extensions.Extension/Uninstall"
-	Extension_Action_FullMethodName    = "/extensions.Extension/Action"
+	ExtensionService_Install_FullMethodName   = "/extensions.ExtensionService/Install"
+	ExtensionService_Uninstall_FullMethodName = "/extensions.ExtensionService/Uninstall"
+	ExtensionService_Check_FullMethodName     = "/extensions.ExtensionService/Check"
+	ExtensionService_Action_FullMethodName    = "/extensions.ExtensionService/Action"
 )
 
-// ExtensionClient is the client API for Extension service.
+// ExtensionServiceClient is the client API for ExtensionService service.
 //
 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
-type ExtensionClient interface {
-	// Для установки расширения выполняется запрос к сервису с указанием расширений которые должны быть установлены. Если
-	// расширение уже установлено процесс возвращает ошибку.
-	Install(ctx context.Context, in *InstallRequest, opts ...grpc.CallOption) (*InstallResponse, error)
-	// Можно запросить проверку статуса установки для расширения. При этом расширение проверяет наличие необходимых данных в
+type ExtensionServiceClient interface {
+	// Install выполняет установку указанных расширений. Если
+	// расширение уже установлены выполняет процесс переустановки. Возвращает longtime операцию
+	Install(ctx context.Context, in *InstallRequest, opts ...grpc.CallOption) (*common.Operation, error)
+	// Uninstall выполняет удаление указанных расширений.
+	Uninstall(ctx context.Context, in *UninstallRequest, opts ...grpc.CallOption) (*common.Operation, error)
+	// Check выполняет проверку установки для расширения. При этом расширение проверяет наличие необходимых данных в
 	// пространстве или наличие новой версии расширения и сообщает об этом. Никаких действий с данными пространства не
 	// производится.
-	Check(ctx context.Context, in *CheckRequest, opts ...grpc.CallOption) (*CheckResponse, error)
-	// Сервис выполняет необходимые действия с данными и миграции для соответствия нужной версии расширения.
-	// Если расширение не установлено процесс обновления возвращает ошибку.
-	Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*UpdateResponse, error)
-	// Удаление расширения из пространства.
-	Uninstall(ctx context.Context, in *UninstallRequest, opts ...grpc.CallOption) (*UninstallResponse, error)
+	Check(ctx context.Context, in *CheckRequest, opts ...grpc.CallOption) (*common.Operation, error)
 	// Пользовательские Действия (Actions)
 	//
 	// Пользовательские действия позволяют расширить функционал пользовательского интерфейса путем
@@ -73,75 +70,63 @@ type ExtensionClient interface {
 	Action(ctx context.Context, in *ActionRequest, opts ...grpc.CallOption) (*ActionResponse, error)
 }
 
-type extensionClient struct {
+type extensionServiceClient struct {
 	cc grpc.ClientConnInterface
 }
 
-func NewExtensionClient(cc grpc.ClientConnInterface) ExtensionClient {
-	return &extensionClient{cc}
+func NewExtensionServiceClient(cc grpc.ClientConnInterface) ExtensionServiceClient {
+	return &extensionServiceClient{cc}
 }
 
-func (c *extensionClient) Install(ctx context.Context, in *InstallRequest, opts ...grpc.CallOption) (*InstallResponse, error) {
-	out := new(InstallResponse)
-	err := c.cc.Invoke(ctx, Extension_Install_FullMethodName, in, out, opts...)
+func (c *extensionServiceClient) Install(ctx context.Context, in *InstallRequest, opts ...grpc.CallOption) (*common.Operation, error) {
+	out := new(common.Operation)
+	err := c.cc.Invoke(ctx, ExtensionService_Install_FullMethodName, in, out, opts...)
 	if err != nil {
 		return nil, err
 	}
 	return out, nil
 }
 
-func (c *extensionClient) Check(ctx context.Context, in *CheckRequest, opts ...grpc.CallOption) (*CheckResponse, error) {
-	out := new(CheckResponse)
-	err := c.cc.Invoke(ctx, Extension_Check_FullMethodName, in, out, opts...)
+func (c *extensionServiceClient) Uninstall(ctx context.Context, in *UninstallRequest, opts ...grpc.CallOption) (*common.Operation, error) {
+	out := new(common.Operation)
+	err := c.cc.Invoke(ctx, ExtensionService_Uninstall_FullMethodName, in, out, opts...)
 	if err != nil {
 		return nil, err
 	}
 	return out, nil
 }
 
-func (c *extensionClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*UpdateResponse, error) {
-	out := new(UpdateResponse)
-	err := c.cc.Invoke(ctx, Extension_Update_FullMethodName, in, out, opts...)
+func (c *extensionServiceClient) Check(ctx context.Context, in *CheckRequest, opts ...grpc.CallOption) (*common.Operation, error) {
+	out := new(common.Operation)
+	err := c.cc.Invoke(ctx, ExtensionService_Check_FullMethodName, in, out, opts...)
 	if err != nil {
 		return nil, err
 	}
 	return out, nil
 }
 
-func (c *extensionClient) Uninstall(ctx context.Context, in *UninstallRequest, opts ...grpc.CallOption) (*UninstallResponse, error) {
-	out := new(UninstallResponse)
-	err := c.cc.Invoke(ctx, Extension_Uninstall_FullMethodName, in, out, opts...)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-func (c *extensionClient) Action(ctx context.Context, in *ActionRequest, opts ...grpc.CallOption) (*ActionResponse, error) {
+func (c *extensionServiceClient) Action(ctx context.Context, in *ActionRequest, opts ...grpc.CallOption) (*ActionResponse, error) {
 	out := new(ActionResponse)
-	err := c.cc.Invoke(ctx, Extension_Action_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, ExtensionService_Action_FullMethodName, in, out, opts...)
 	if err != nil {
 		return nil, err
 	}
 	return out, nil
 }
 
-// ExtensionServer is the server API for Extension service.
-// All implementations must embed UnimplementedExtensionServer
+// ExtensionServiceServer is the server API for ExtensionService service.
+// All implementations must embed UnimplementedExtensionServiceServer
 // for forward compatibility
-type ExtensionServer interface {
-	// Для установки расширения выполняется запрос к сервису с указанием расширений которые должны быть установлены. Если
-	// расширение уже установлено процесс возвращает ошибку.
-	Install(context.Context, *InstallRequest) (*InstallResponse, error)
-	// Можно запросить проверку статуса установки для расширения. При этом расширение проверяет наличие необходимых данных в
+type ExtensionServiceServer interface {
+	// Install выполняет установку указанных расширений. Если
+	// расширение уже установлены выполняет процесс переустановки. Возвращает longtime операцию
+	Install(context.Context, *InstallRequest) (*common.Operation, error)
+	// Uninstall выполняет удаление указанных расширений.
+	Uninstall(context.Context, *UninstallRequest) (*common.Operation, error)
+	// Check выполняет проверку установки для расширения. При этом расширение проверяет наличие необходимых данных в
 	// пространстве или наличие новой версии расширения и сообщает об этом. Никаких действий с данными пространства не
 	// производится.
-	Check(context.Context, *CheckRequest) (*CheckResponse, error)
-	// Сервис выполняет необходимые действия с данными и миграции для соответствия нужной версии расширения.
-	// Если расширение не установлено процесс обновления возвращает ошибку.
-	Update(context.Context, *UpdateRequest) (*UpdateResponse, error)
-	// Удаление расширения из пространства.
-	Uninstall(context.Context, *UninstallRequest) (*UninstallResponse, error)
+	Check(context.Context, *CheckRequest) (*common.Operation, error)
 	// Пользовательские Действия (Actions)
 	//
 	// Пользовательские действия позволяют расширить функционал пользовательского интерфейса путем
@@ -161,159 +146,134 @@ type ExtensionServer interface {
 	// Приложения так же могут использовать действия для вызова обработки в других приложениях при
 	// необходимости.
 	Action(context.Context, *ActionRequest) (*ActionResponse, error)
-	mustEmbedUnimplementedExtensionServer()
+	mustEmbedUnimplementedExtensionServiceServer()
 }
 
-// UnimplementedExtensionServer must be embedded to have forward compatible implementations.
-type UnimplementedExtensionServer struct {
+// UnimplementedExtensionServiceServer must be embedded to have forward compatible implementations.
+type UnimplementedExtensionServiceServer struct {
 }
 
-func (UnimplementedExtensionServer) Install(context.Context, *InstallRequest) (*InstallResponse, error) {
+func (UnimplementedExtensionServiceServer) Install(context.Context, *InstallRequest) (*common.Operation, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Install not implemented")
 }
-func (UnimplementedExtensionServer) Check(context.Context, *CheckRequest) (*CheckResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method Check not implemented")
-}
-func (UnimplementedExtensionServer) Update(context.Context, *UpdateRequest) (*UpdateResponse, error) {
-	return nil, status.Errorf(codes.Unimplemented, "method Update not implemented")
-}
-func (UnimplementedExtensionServer) Uninstall(context.Context, *UninstallRequest) (*UninstallResponse, error) {
+func (UnimplementedExtensionServiceServer) Uninstall(context.Context, *UninstallRequest) (*common.Operation, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Uninstall not implemented")
 }
-func (UnimplementedExtensionServer) Action(context.Context, *ActionRequest) (*ActionResponse, error) {
+func (UnimplementedExtensionServiceServer) Check(context.Context, *CheckRequest) (*common.Operation, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Check not implemented")
+}
+func (UnimplementedExtensionServiceServer) Action(context.Context, *ActionRequest) (*ActionResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method Action not implemented")
 }
-func (UnimplementedExtensionServer) mustEmbedUnimplementedExtensionServer() {}
+func (UnimplementedExtensionServiceServer) mustEmbedUnimplementedExtensionServiceServer() {}
 
-// UnsafeExtensionServer may be embedded to opt out of forward compatibility for this service.
-// Use of this interface is not recommended, as added methods to ExtensionServer will
+// UnsafeExtensionServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to ExtensionServiceServer will
 // result in compilation errors.
-type UnsafeExtensionServer interface {
-	mustEmbedUnimplementedExtensionServer()
+type UnsafeExtensionServiceServer interface {
+	mustEmbedUnimplementedExtensionServiceServer()
 }
 
-func RegisterExtensionServer(s grpc.ServiceRegistrar, srv ExtensionServer) {
-	s.RegisterService(&Extension_ServiceDesc, srv)
+func RegisterExtensionServiceServer(s grpc.ServiceRegistrar, srv ExtensionServiceServer) {
+	s.RegisterService(&ExtensionService_ServiceDesc, srv)
 }
 
-func _Extension_Install_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+func _ExtensionService_Install_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(InstallRequest)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(ExtensionServer).Install(ctx, in)
-	}
-	info := &grpc.UnaryServerInfo{
-		Server:     srv,
-		FullMethod: Extension_Install_FullMethodName,
-	}
-	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ExtensionServer).Install(ctx, req.(*InstallRequest))
-	}
-	return interceptor(ctx, in, info, handler)
-}
-
-func _Extension_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(CheckRequest)
-	if err := dec(in); err != nil {
-		return nil, err
-	}
-	if interceptor == nil {
-		return srv.(ExtensionServer).Check(ctx, in)
+		return srv.(ExtensionServiceServer).Install(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: Extension_Check_FullMethodName,
+		FullMethod: ExtensionService_Install_FullMethodName,
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ExtensionServer).Check(ctx, req.(*CheckRequest))
+		return srv.(ExtensionServiceServer).Install(ctx, req.(*InstallRequest))
 	}
 	return interceptor(ctx, in, info, handler)
 }
 
-func _Extension_Update_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(UpdateRequest)
+func _ExtensionService_Uninstall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(UninstallRequest)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(ExtensionServer).Update(ctx, in)
+		return srv.(ExtensionServiceServer).Uninstall(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: Extension_Update_FullMethodName,
+		FullMethod: ExtensionService_Uninstall_FullMethodName,
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ExtensionServer).Update(ctx, req.(*UpdateRequest))
+		return srv.(ExtensionServiceServer).Uninstall(ctx, req.(*UninstallRequest))
 	}
 	return interceptor(ctx, in, info, handler)
 }
 
-func _Extension_Uninstall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
-	in := new(UninstallRequest)
+func _ExtensionService_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(CheckRequest)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(ExtensionServer).Uninstall(ctx, in)
+		return srv.(ExtensionServiceServer).Check(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: Extension_Uninstall_FullMethodName,
+		FullMethod: ExtensionService_Check_FullMethodName,
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ExtensionServer).Uninstall(ctx, req.(*UninstallRequest))
+		return srv.(ExtensionServiceServer).Check(ctx, req.(*CheckRequest))
 	}
 	return interceptor(ctx, in, info, handler)
 }
 
-func _Extension_Action_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+func _ExtensionService_Action_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(ActionRequest)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(ExtensionServer).Action(ctx, in)
+		return srv.(ExtensionServiceServer).Action(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: Extension_Action_FullMethodName,
+		FullMethod: ExtensionService_Action_FullMethodName,
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ExtensionServer).Action(ctx, req.(*ActionRequest))
+		return srv.(ExtensionServiceServer).Action(ctx, req.(*ActionRequest))
 	}
 	return interceptor(ctx, in, info, handler)
 }
 
-// Extension_ServiceDesc is the grpc.ServiceDesc for Extension service.
+// ExtensionService_ServiceDesc is the grpc.ServiceDesc for ExtensionService service.
 // It's only intended for direct use with grpc.RegisterService,
 // and not to be introspected or modified (even as a copy)
-var Extension_ServiceDesc = grpc.ServiceDesc{
-	ServiceName: "extensions.Extension",
-	HandlerType: (*ExtensionServer)(nil),
+var ExtensionService_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "extensions.ExtensionService",
+	HandlerType: (*ExtensionServiceServer)(nil),
 	Methods: []grpc.MethodDesc{
 		{
 			MethodName: "Install",
-			Handler:    _Extension_Install_Handler,
-		},
-		{
-			MethodName: "Check",
-			Handler:    _Extension_Check_Handler,
+			Handler:    _ExtensionService_Install_Handler,
 		},
 		{
-			MethodName: "Update",
-			Handler:    _Extension_Update_Handler,
+			MethodName: "Uninstall",
+			Handler:    _ExtensionService_Uninstall_Handler,
 		},
 		{
-			MethodName: "Uninstall",
-			Handler:    _Extension_Uninstall_Handler,
+			MethodName: "Check",
+			Handler:    _ExtensionService_Check_Handler,
 		},
 		{
 			MethodName: "Action",
-			Handler:    _Extension_Action_Handler,
+			Handler:    _ExtensionService_Action_Handler,
 		},
 	},
 	Streams:  []grpc.StreamDesc{},
-	Metadata: "extensions/extension.proto",
+	Metadata: "extensions/extension_service.proto",
 }
diff --git a/proto/extensions/manager.pb.go b/proto/extensions/manager.pb.go
deleted file mode 100644
index 0cffc9ab790950d5ac7383c30f0f08d5b1ae85d5..0000000000000000000000000000000000000000
--- a/proto/extensions/manager.pb.go
+++ /dev/null
@@ -1,898 +0,0 @@
-//*
-//
-//# Менеджер расширений
-//
-//Реализует функционал по координации расширений в системе и служит единой точкой доступа для работы с расширениями:
-//- Регистрирует все расширения в едином реестре
-//- Предоставляет доступ к реестру расширений на получение доступных расширений в системе
-//- Контролирует процесс установки расширения, устанавливает все необходимые зависимости в правильном порядке
-//- Контролирует создание системной коллекции `system_extensions` (System/Extensions) и создание/удалений в ней установленных расширений
-//- Контролирует создание системной коллекции `system_actions` (System/Actions). Выполняет удаление всех действий связанных
-//с расширением при его удалении.
-//- Выполняет переадресацию запросов на действия для расширений
-//
-//Менеджер расширений для контроля установленных расширений использует системные коллекции. При обращении к сервису контента
-//используются системный уровень прав для создания необходимых коллекций и получения данных:
-//1. `system_extensions` (System Extensions) - доступна только системным пользователям
-//2. `system_actions` (System Actions) - по умолчанию доступна только системным пользователям, настройки доступа на чтение
-//выдаются пользователям вручную. В зависимости от пользователя в интерфейсе отображаются те или иные доступные действия.
-//
-//Проверка наличия коллекций и создание в случае их отсутствия выполняется в момент установки расширений. После установки
-//расширения в коллекцию `system_extensions` добавляется запись об установленном расширении. Действия добавляются в
-//коллекцию `system_actions` расширениями по собственному усмотрению при установке.
-//
-//Для обращения к расширениям используются права текущего пользователя запрашивающего операцию. Если пользователь не
-//обладает необходимыми правами для выполнения действия то операция вернет ошибку.
-//
-
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// versions:
-// 	protoc-gen-go v1.30.0
-// 	protoc        v4.23.4
-// source: extensions/manager.proto
-
-package extensions
-
-import (
-	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
-	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
-	reflect "reflect"
-	sync "sync"
-)
-
-const (
-	// Verify that this generated code is sufficiently up-to-date.
-	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
-	// Verify that runtime/protoimpl is sufficiently up-to-date.
-	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
-)
-
-type SpaceExtensions_State int32
-
-const (
-	SpaceExtensions_PENDING     SpaceExtensions_State = 0
-	SpaceExtensions_INSTALLED   SpaceExtensions_State = 1
-	SpaceExtensions_IN_PROGRESS SpaceExtensions_State = 2
-	SpaceExtensions_FAIL        SpaceExtensions_State = 3
-)
-
-// Enum value maps for SpaceExtensions_State.
-var (
-	SpaceExtensions_State_name = map[int32]string{
-		0: "PENDING",
-		1: "INSTALLED",
-		2: "IN_PROGRESS",
-		3: "FAIL",
-	}
-	SpaceExtensions_State_value = map[string]int32{
-		"PENDING":     0,
-		"INSTALLED":   1,
-		"IN_PROGRESS": 2,
-		"FAIL":        3,
-	}
-)
-
-func (x SpaceExtensions_State) Enum() *SpaceExtensions_State {
-	p := new(SpaceExtensions_State)
-	*p = x
-	return p
-}
-
-func (x SpaceExtensions_State) String() string {
-	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
-}
-
-func (SpaceExtensions_State) Descriptor() protoreflect.EnumDescriptor {
-	return file_extensions_manager_proto_enumTypes[0].Descriptor()
-}
-
-func (SpaceExtensions_State) Type() protoreflect.EnumType {
-	return &file_extensions_manager_proto_enumTypes[0]
-}
-
-func (x SpaceExtensions_State) Number() protoreflect.EnumNumber {
-	return protoreflect.EnumNumber(x)
-}
-
-// Deprecated: Use SpaceExtensions_State.Descriptor instead.
-func (SpaceExtensions_State) EnumDescriptor() ([]byte, []int) {
-	return file_extensions_manager_proto_rawDescGZIP(), []int{8, 0}
-}
-
-// ServiceDescription описание сервиса
-type ExtensionDescriptor struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Extension          string            `protobuf:"bytes,10000,opt,name=extension,proto3" json:"extension,omitempty"`                                                                                   // Имя расширения
-	Title              string            `protobuf:"bytes,10010,opt,name=title,proto3" json:"title,omitempty"`                                                                                           // Название расширения
-	Description        string            `protobuf:"bytes,10020,opt,name=description,proto3" json:"description,omitempty"`                                                                               // Описание расширения
-	Version            string            `protobuf:"bytes,10100,opt,name=version,proto3" json:"version,omitempty"`                                                                                       // Версия расширения
-	VersionDescription string            `protobuf:"bytes,10110,opt,name=version_description,json=versionDescription,proto3" json:"version_description,omitempty"`                                       // Описание версии
-	Deps               []string          `protobuf:"bytes,10200,rep,name=deps,proto3" json:"deps,omitempty"`                                                                                             // Зависимости от других расширений
-	Url                string            `protobuf:"bytes,5,opt,name=url,proto3" json:"url,omitempty"`                                                                                                   // Адрес для внутри сети, коммуникация между сервисами
-	Metadata           map[string]string `protobuf:"bytes,6,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Метаданные сервиса, смотрите документацию по сервису
-}
-
-func (x *ExtensionDescriptor) Reset() {
-	*x = ExtensionDescriptor{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_manager_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *ExtensionDescriptor) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*ExtensionDescriptor) ProtoMessage() {}
-
-func (x *ExtensionDescriptor) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_manager_proto_msgTypes[0]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use ExtensionDescriptor.ProtoReflect.Descriptor instead.
-func (*ExtensionDescriptor) Descriptor() ([]byte, []int) {
-	return file_extensions_manager_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *ExtensionDescriptor) GetExtension() string {
-	if x != nil {
-		return x.Extension
-	}
-	return ""
-}
-
-func (x *ExtensionDescriptor) GetTitle() string {
-	if x != nil {
-		return x.Title
-	}
-	return ""
-}
-
-func (x *ExtensionDescriptor) GetDescription() string {
-	if x != nil {
-		return x.Description
-	}
-	return ""
-}
-
-func (x *ExtensionDescriptor) GetVersion() string {
-	if x != nil {
-		return x.Version
-	}
-	return ""
-}
-
-func (x *ExtensionDescriptor) GetVersionDescription() string {
-	if x != nil {
-		return x.VersionDescription
-	}
-	return ""
-}
-
-func (x *ExtensionDescriptor) GetDeps() []string {
-	if x != nil {
-		return x.Deps
-	}
-	return nil
-}
-
-func (x *ExtensionDescriptor) GetUrl() string {
-	if x != nil {
-		return x.Url
-	}
-	return ""
-}
-
-func (x *ExtensionDescriptor) GetMetadata() map[string]string {
-	if x != nil {
-		return x.Metadata
-	}
-	return nil
-}
-
-type RegisterExtensionsRequest struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Extensions []*ExtensionDescriptor `protobuf:"bytes,1,rep,name=extensions,proto3" json:"extensions,omitempty"`
-}
-
-func (x *RegisterExtensionsRequest) Reset() {
-	*x = RegisterExtensionsRequest{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_manager_proto_msgTypes[1]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *RegisterExtensionsRequest) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*RegisterExtensionsRequest) ProtoMessage() {}
-
-func (x *RegisterExtensionsRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_manager_proto_msgTypes[1]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use RegisterExtensionsRequest.ProtoReflect.Descriptor instead.
-func (*RegisterExtensionsRequest) Descriptor() ([]byte, []int) {
-	return file_extensions_manager_proto_rawDescGZIP(), []int{1}
-}
-
-func (x *RegisterExtensionsRequest) GetExtensions() []*ExtensionDescriptor {
-	if x != nil {
-		return x.Extensions
-	}
-	return nil
-}
-
-type RegisterExtensionsResponse struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-}
-
-func (x *RegisterExtensionsResponse) Reset() {
-	*x = RegisterExtensionsResponse{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_manager_proto_msgTypes[2]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *RegisterExtensionsResponse) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*RegisterExtensionsResponse) ProtoMessage() {}
-
-func (x *RegisterExtensionsResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_manager_proto_msgTypes[2]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use RegisterExtensionsResponse.ProtoReflect.Descriptor instead.
-func (*RegisterExtensionsResponse) Descriptor() ([]byte, []int) {
-	return file_extensions_manager_proto_rawDescGZIP(), []int{2}
-}
-
-type UnregisterExtensionsRequest struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Extensions []*ExtensionDescriptor `protobuf:"bytes,1,rep,name=extensions,proto3" json:"extensions,omitempty"`
-}
-
-func (x *UnregisterExtensionsRequest) Reset() {
-	*x = UnregisterExtensionsRequest{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_manager_proto_msgTypes[3]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *UnregisterExtensionsRequest) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*UnregisterExtensionsRequest) ProtoMessage() {}
-
-func (x *UnregisterExtensionsRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_manager_proto_msgTypes[3]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use UnregisterExtensionsRequest.ProtoReflect.Descriptor instead.
-func (*UnregisterExtensionsRequest) Descriptor() ([]byte, []int) {
-	return file_extensions_manager_proto_rawDescGZIP(), []int{3}
-}
-
-func (x *UnregisterExtensionsRequest) GetExtensions() []*ExtensionDescriptor {
-	if x != nil {
-		return x.Extensions
-	}
-	return nil
-}
-
-type UnregisterExtensionsResponse struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-}
-
-func (x *UnregisterExtensionsResponse) Reset() {
-	*x = UnregisterExtensionsResponse{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_manager_proto_msgTypes[4]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *UnregisterExtensionsResponse) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*UnregisterExtensionsResponse) ProtoMessage() {}
-
-func (x *UnregisterExtensionsResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_manager_proto_msgTypes[4]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use UnregisterExtensionsResponse.ProtoReflect.Descriptor instead.
-func (*UnregisterExtensionsResponse) Descriptor() ([]byte, []int) {
-	return file_extensions_manager_proto_rawDescGZIP(), []int{4}
-}
-
-type ListExtensionsFilter struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Extension []string `protobuf:"bytes,1,rep,name=extension,proto3" json:"extension,omitempty"` // Список имен сервисов для получения результатов. Список может содержать регулярные выражения.
-}
-
-func (x *ListExtensionsFilter) Reset() {
-	*x = ListExtensionsFilter{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_manager_proto_msgTypes[5]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *ListExtensionsFilter) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*ListExtensionsFilter) ProtoMessage() {}
-
-func (x *ListExtensionsFilter) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_manager_proto_msgTypes[5]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use ListExtensionsFilter.ProtoReflect.Descriptor instead.
-func (*ListExtensionsFilter) Descriptor() ([]byte, []int) {
-	return file_extensions_manager_proto_rawDescGZIP(), []int{5}
-}
-
-func (x *ListExtensionsFilter) GetExtension() []string {
-	if x != nil {
-		return x.Extension
-	}
-	return nil
-}
-
-type ListExtensionsRequest struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Filter *ListExtensionsFilter `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"`
-}
-
-func (x *ListExtensionsRequest) Reset() {
-	*x = ListExtensionsRequest{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_manager_proto_msgTypes[6]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *ListExtensionsRequest) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*ListExtensionsRequest) ProtoMessage() {}
-
-func (x *ListExtensionsRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_manager_proto_msgTypes[6]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use ListExtensionsRequest.ProtoReflect.Descriptor instead.
-func (*ListExtensionsRequest) Descriptor() ([]byte, []int) {
-	return file_extensions_manager_proto_rawDescGZIP(), []int{6}
-}
-
-func (x *ListExtensionsRequest) GetFilter() *ListExtensionsFilter {
-	if x != nil {
-		return x.Filter
-	}
-	return nil
-}
-
-type ListExtensionsResponse struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Extensions []*ExtensionDescriptor `protobuf:"bytes,1,rep,name=extensions,proto3" json:"extensions,omitempty"`
-}
-
-func (x *ListExtensionsResponse) Reset() {
-	*x = ListExtensionsResponse{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_manager_proto_msgTypes[7]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *ListExtensionsResponse) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*ListExtensionsResponse) ProtoMessage() {}
-
-func (x *ListExtensionsResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_manager_proto_msgTypes[7]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use ListExtensionsResponse.ProtoReflect.Descriptor instead.
-func (*ListExtensionsResponse) Descriptor() ([]byte, []int) {
-	return file_extensions_manager_proto_rawDescGZIP(), []int{7}
-}
-
-func (x *ListExtensionsResponse) GetExtensions() []*ExtensionDescriptor {
-	if x != nil {
-		return x.Extensions
-	}
-	return nil
-}
-
-type SpaceExtensions struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	Extension   string                `protobuf:"bytes,10000,opt,name=extension,proto3" json:"extension,omitempty"`
-	Title       string                `protobuf:"bytes,10010,opt,name=title,proto3" json:"title,omitempty"`
-	Version     string                `protobuf:"bytes,10100,opt,name=version,proto3" json:"version,omitempty"`
-	Deps        []string              `protobuf:"bytes,10200,rep,name=deps,proto3" json:"deps,omitempty"`
-	State       SpaceExtensions_State `protobuf:"varint,10300,opt,name=state,proto3,enum=extensions.SpaceExtensions_State" json:"state,omitempty"`
-	StatusError string                `protobuf:"bytes,10400,opt,name=status_error,json=statusError,proto3" json:"status_error,omitempty"` // Сообщение к ошибке
-	StatusMsg   string                `protobuf:"bytes,10500,opt,name=status_msg,json=statusMsg,proto3" json:"status_msg,omitempty"`       // Сообщение к статусу
-}
-
-func (x *SpaceExtensions) Reset() {
-	*x = SpaceExtensions{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_extensions_manager_proto_msgTypes[8]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *SpaceExtensions) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*SpaceExtensions) ProtoMessage() {}
-
-func (x *SpaceExtensions) ProtoReflect() protoreflect.Message {
-	mi := &file_extensions_manager_proto_msgTypes[8]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use SpaceExtensions.ProtoReflect.Descriptor instead.
-func (*SpaceExtensions) Descriptor() ([]byte, []int) {
-	return file_extensions_manager_proto_rawDescGZIP(), []int{8}
-}
-
-func (x *SpaceExtensions) GetExtension() string {
-	if x != nil {
-		return x.Extension
-	}
-	return ""
-}
-
-func (x *SpaceExtensions) GetTitle() string {
-	if x != nil {
-		return x.Title
-	}
-	return ""
-}
-
-func (x *SpaceExtensions) GetVersion() string {
-	if x != nil {
-		return x.Version
-	}
-	return ""
-}
-
-func (x *SpaceExtensions) GetDeps() []string {
-	if x != nil {
-		return x.Deps
-	}
-	return nil
-}
-
-func (x *SpaceExtensions) GetState() SpaceExtensions_State {
-	if x != nil {
-		return x.State
-	}
-	return SpaceExtensions_PENDING
-}
-
-func (x *SpaceExtensions) GetStatusError() string {
-	if x != nil {
-		return x.StatusError
-	}
-	return ""
-}
-
-func (x *SpaceExtensions) GetStatusMsg() string {
-	if x != nil {
-		return x.StatusMsg
-	}
-	return ""
-}
-
-var File_extensions_manager_proto protoreflect.FileDescriptor
-
-var file_extensions_manager_proto_rawDesc = []byte{
-	0x0a, 0x18, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x6d, 0x61, 0x6e,
-	0x61, 0x67, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x65, 0x78, 0x74, 0x65,
-	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xea, 0x02, 0x0a, 0x13, 0x45, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x1d,
-	0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x90, 0x4e, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a,
-	0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x9a, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74,
-	0x69, 0x74, 0x6c, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x18, 0xa4, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63,
-	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
-	0x6f, 0x6e, 0x18, 0xf4, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
-	0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x13, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x65,
-	0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xfe, 0x4e, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x12, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
-	0x74, 0x69, 0x6f, 0x6e, 0x12, 0x13, 0x0a, 0x04, 0x64, 0x65, 0x70, 0x73, 0x18, 0xd8, 0x4f, 0x20,
-	0x03, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c,
-	0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x49, 0x0a, 0x08, 0x6d,
-	0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e,
-	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x4d,
-	0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65,
-	0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
-	0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
-	0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
-	0x02, 0x38, 0x01, 0x22, 0x5c, 0x0a, 0x19, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45,
-	0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x12, 0x3f, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01,
-	0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
-	0x73, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72,
-	0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
-	0x73, 0x22, 0x1c, 0x0a, 0x1a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74,
-	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
-	0x5e, 0x0a, 0x1b, 0x55, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74,
-	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f,
-	0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03,
-	0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
-	0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
-	0x74, 0x6f, 0x72, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22,
-	0x1e, 0x0a, 0x1c, 0x55, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74,
-	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
-	0x34, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
-	0x73, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65,
-	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x51, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x74,
-	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38,
-	0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20,
-	0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74,
-	0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72,
-	0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x59, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74,
-	0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
-	0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
-	0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73,
-	0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
-	0x6f, 0x6e, 0x73, 0x22, 0xb5, 0x02, 0x0a, 0x0f, 0x53, 0x70, 0x61, 0x63, 0x65, 0x45, 0x78, 0x74,
-	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x18, 0x90, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x74,
-	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18,
-	0x9a, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x19, 0x0a,
-	0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0xf4, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x13, 0x0a, 0x04, 0x64, 0x65, 0x70, 0x73,
-	0x18, 0xd8, 0x4f, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73, 0x12, 0x38, 0x0a,
-	0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0xbc, 0x50, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e,
-	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x70, 0x61, 0x63, 0x65,
-	0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65,
-	0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75,
-	0x73, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xa0, 0x51, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
-	0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x73,
-	0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x84, 0x52, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d, 0x73, 0x67, 0x22, 0x3e, 0x0a, 0x05, 0x53,
-	0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10,
-	0x00, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x01,
-	0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10,
-	0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x03, 0x32, 0xc1, 0x02, 0x0a, 0x10,
-	0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72,
-	0x12, 0x65, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65,
-	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
-	0x6f, 0x6e, 0x73, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65,
-	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e,
-	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73,
-	0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x14, 0x55, 0x6e, 0x72, 0x65, 0x67,
-	0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12,
-	0x27, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x55, 0x6e, 0x72,
-	0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
-	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x55, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
-	0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65,
-	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
-	0x6f, 0x6e, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
-	0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x65, 0x78, 0x74, 0x65,
-	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e,
-	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42,
-	0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x2e, 0x70, 0x65, 0x72, 0x78, 0x2e, 0x72, 0x75, 0x2f, 0x70,
-	0x65, 0x72, 0x78, 0x69, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2d, 0x67, 0x6f, 0x2f,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
-	0x3b, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x33,
-}
-
-var (
-	file_extensions_manager_proto_rawDescOnce sync.Once
-	file_extensions_manager_proto_rawDescData = file_extensions_manager_proto_rawDesc
-)
-
-func file_extensions_manager_proto_rawDescGZIP() []byte {
-	file_extensions_manager_proto_rawDescOnce.Do(func() {
-		file_extensions_manager_proto_rawDescData = protoimpl.X.CompressGZIP(file_extensions_manager_proto_rawDescData)
-	})
-	return file_extensions_manager_proto_rawDescData
-}
-
-var file_extensions_manager_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_extensions_manager_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
-var file_extensions_manager_proto_goTypes = []interface{}{
-	(SpaceExtensions_State)(0),           // 0: extensions.SpaceExtensions.State
-	(*ExtensionDescriptor)(nil),          // 1: extensions.ExtensionDescriptor
-	(*RegisterExtensionsRequest)(nil),    // 2: extensions.RegisterExtensionsRequest
-	(*RegisterExtensionsResponse)(nil),   // 3: extensions.RegisterExtensionsResponse
-	(*UnregisterExtensionsRequest)(nil),  // 4: extensions.UnregisterExtensionsRequest
-	(*UnregisterExtensionsResponse)(nil), // 5: extensions.UnregisterExtensionsResponse
-	(*ListExtensionsFilter)(nil),         // 6: extensions.ListExtensionsFilter
-	(*ListExtensionsRequest)(nil),        // 7: extensions.ListExtensionsRequest
-	(*ListExtensionsResponse)(nil),       // 8: extensions.ListExtensionsResponse
-	(*SpaceExtensions)(nil),              // 9: extensions.SpaceExtensions
-	nil,                                  // 10: extensions.ExtensionDescriptor.MetadataEntry
-}
-var file_extensions_manager_proto_depIdxs = []int32{
-	10, // 0: extensions.ExtensionDescriptor.metadata:type_name -> extensions.ExtensionDescriptor.MetadataEntry
-	1,  // 1: extensions.RegisterExtensionsRequest.extensions:type_name -> extensions.ExtensionDescriptor
-	1,  // 2: extensions.UnregisterExtensionsRequest.extensions:type_name -> extensions.ExtensionDescriptor
-	6,  // 3: extensions.ListExtensionsRequest.filter:type_name -> extensions.ListExtensionsFilter
-	1,  // 4: extensions.ListExtensionsResponse.extensions:type_name -> extensions.ExtensionDescriptor
-	0,  // 5: extensions.SpaceExtensions.state:type_name -> extensions.SpaceExtensions.State
-	2,  // 6: extensions.ExtensionManager.RegisterExtensions:input_type -> extensions.RegisterExtensionsRequest
-	4,  // 7: extensions.ExtensionManager.UnregisterExtensions:input_type -> extensions.UnregisterExtensionsRequest
-	7,  // 8: extensions.ExtensionManager.ListExtensions:input_type -> extensions.ListExtensionsRequest
-	3,  // 9: extensions.ExtensionManager.RegisterExtensions:output_type -> extensions.RegisterExtensionsResponse
-	5,  // 10: extensions.ExtensionManager.UnregisterExtensions:output_type -> extensions.UnregisterExtensionsResponse
-	8,  // 11: extensions.ExtensionManager.ListExtensions:output_type -> extensions.ListExtensionsResponse
-	9,  // [9:12] is the sub-list for method output_type
-	6,  // [6:9] is the sub-list for method input_type
-	6,  // [6:6] is the sub-list for extension type_name
-	6,  // [6:6] is the sub-list for extension extendee
-	0,  // [0:6] is the sub-list for field type_name
-}
-
-func init() { file_extensions_manager_proto_init() }
-func file_extensions_manager_proto_init() {
-	if File_extensions_manager_proto != nil {
-		return
-	}
-	if !protoimpl.UnsafeEnabled {
-		file_extensions_manager_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ExtensionDescriptor); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_manager_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*RegisterExtensionsRequest); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_manager_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*RegisterExtensionsResponse); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_manager_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*UnregisterExtensionsRequest); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_manager_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*UnregisterExtensionsResponse); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_manager_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ListExtensionsFilter); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_manager_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ListExtensionsRequest); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_manager_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ListExtensionsResponse); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_extensions_manager_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*SpaceExtensions); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-	}
-	type x struct{}
-	out := protoimpl.TypeBuilder{
-		File: protoimpl.DescBuilder{
-			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
-			RawDescriptor: file_extensions_manager_proto_rawDesc,
-			NumEnums:      1,
-			NumMessages:   10,
-			NumExtensions: 0,
-			NumServices:   1,
-		},
-		GoTypes:           file_extensions_manager_proto_goTypes,
-		DependencyIndexes: file_extensions_manager_proto_depIdxs,
-		EnumInfos:         file_extensions_manager_proto_enumTypes,
-		MessageInfos:      file_extensions_manager_proto_msgTypes,
-	}.Build()
-	File_extensions_manager_proto = out.File
-	file_extensions_manager_proto_rawDesc = nil
-	file_extensions_manager_proto_goTypes = nil
-	file_extensions_manager_proto_depIdxs = nil
-}
diff --git a/proto/extensions/manager_service.pb.go b/proto/extensions/manager_service.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..47252630d183c6075e2d5d15644818b650ccf6e4
--- /dev/null
+++ b/proto/extensions/manager_service.pb.go
@@ -0,0 +1,1219 @@
+//*
+//
+//# Менеджер расширений
+//
+//Реализует функционал по координации расширений в системе и служит единой точкой доступа для работы с расширениями:
+//- Регистрирует все расширения в едином реестре
+//- Предоставляет доступ к реестру расширений на получение доступных расширений в системе
+//- Контролирует процесс установки расширения, устанавливает все необходимые зависимости в правильном порядке
+//- Контролирует создание системной коллекции `system_extensions` (System/Extensions) и создание/удалений в ней установленных расширений
+//- Контролирует создание системной коллекции `system_actions` (System/Actions). Выполняет удаление всех действий связанных
+//с расширением при его удалении.
+//- Выполняет переадресацию запросов на действия для расширений
+//
+//Менеджер расширений для контроля установленных расширений использует системные коллекции. При обращении к сервису контента
+//используются системный уровень прав для создания необходимых коллекций и получения данных:
+//1. `system_extensions` (System Extensions) - доступна только системным пользователям
+//2. `system_actions` (System Actions) - по умолчанию доступна только системным пользователям, настройки доступа на чтение
+//выдаются пользователям вручную. В зависимости от пользователя в интерфейсе отображаются те или иные доступные действия.
+//
+//Проверка наличия коллекций и создание в случае их отсутствия выполняется в момент установки расширений. После установки
+//расширения в коллекцию `system_extensions` добавляется запись об установленном расширении. Действия добавляются в
+//коллекцию `system_actions` расширениями по собственному усмотрению при установке.
+//
+//Для обращения к расширениям используются права текущего пользователя запрашивающего операцию. Если пользователь не
+//обладает необходимыми правами для выполнения действия то операция вернет ошибку.
+//
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
+// source: extensions/manager_service.proto
+
+package extensions
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type State int32
+
+const (
+	State_UNKNOWN     State = 0
+	State_INSTALLED   State = 1
+	State_IN_PROGRESS State = 2
+	State_FAIL        State = 3
+	State_PENDING     State = 4
+)
+
+// Enum value maps for State.
+var (
+	State_name = map[int32]string{
+		0: "UNKNOWN",
+		1: "INSTALLED",
+		2: "IN_PROGRESS",
+		3: "FAIL",
+		4: "PENDING",
+	}
+	State_value = map[string]int32{
+		"UNKNOWN":     0,
+		"INSTALLED":   1,
+		"IN_PROGRESS": 2,
+		"FAIL":        3,
+		"PENDING":     4,
+	}
+)
+
+func (x State) Enum() *State {
+	p := new(State)
+	*p = x
+	return p
+}
+
+func (x State) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (State) Descriptor() protoreflect.EnumDescriptor {
+	return file_extensions_manager_service_proto_enumTypes[0].Descriptor()
+}
+
+func (State) Type() protoreflect.EnumType {
+	return &file_extensions_manager_service_proto_enumTypes[0]
+}
+
+func (x State) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use State.Descriptor instead.
+func (State) EnumDescriptor() ([]byte, []int) {
+	return file_extensions_manager_service_proto_rawDescGZIP(), []int{0}
+}
+
+// GetInstalledExtensionsRequest - запрос на получение статуса расширений в пространстве и окружении.
+// Если список расширений в запросе не передан, возвращаются все установленные расширения
+type GetInstalledExtensionsRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Extensions []string `protobuf:"bytes,1,rep,name=extensions,proto3" json:"extensions,omitempty"`          // Имя расширения
+	SpaceId    string   `protobuf:"bytes,2,opt,name=space_id,json=spaceId,proto3" json:"space_id,omitempty"` // Пространство имен расширения
+	EnvId      string   `protobuf:"bytes,3,opt,name=env_id,json=envId,proto3" json:"env_id,omitempty"`       // Идентификатор окружения
+}
+
+func (x *GetInstalledExtensionsRequest) Reset() {
+	*x = GetInstalledExtensionsRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_manager_service_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetInstalledExtensionsRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetInstalledExtensionsRequest) ProtoMessage() {}
+
+func (x *GetInstalledExtensionsRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_manager_service_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetInstalledExtensionsRequest.ProtoReflect.Descriptor instead.
+func (*GetInstalledExtensionsRequest) Descriptor() ([]byte, []int) {
+	return file_extensions_manager_service_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *GetInstalledExtensionsRequest) GetExtensions() []string {
+	if x != nil {
+		return x.Extensions
+	}
+	return nil
+}
+
+func (x *GetInstalledExtensionsRequest) GetSpaceId() string {
+	if x != nil {
+		return x.SpaceId
+	}
+	return ""
+}
+
+func (x *GetInstalledExtensionsRequest) GetEnvId() string {
+	if x != nil {
+		return x.EnvId
+	}
+	return ""
+}
+
+// GetExtensionResponse - описание расширения
+type GetInstalledExtensionsResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Status []*GetInstalledExtensionsResponse_Status `protobuf:"bytes,1,rep,name=status,proto3" json:"status,omitempty"`
+}
+
+func (x *GetInstalledExtensionsResponse) Reset() {
+	*x = GetInstalledExtensionsResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_manager_service_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetInstalledExtensionsResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetInstalledExtensionsResponse) ProtoMessage() {}
+
+func (x *GetInstalledExtensionsResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_manager_service_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetInstalledExtensionsResponse.ProtoReflect.Descriptor instead.
+func (*GetInstalledExtensionsResponse) Descriptor() ([]byte, []int) {
+	return file_extensions_manager_service_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *GetInstalledExtensionsResponse) GetStatus() []*GetInstalledExtensionsResponse_Status {
+	if x != nil {
+		return x.Status
+	}
+	return nil
+}
+
+// ExtensionDescriptor описание расширения (возвращается сервисом при регистрации)
+type ExtensionDescriptor struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Extension          string            `protobuf:"bytes,10000,opt,name=extension,proto3" json:"extension,omitempty"`                                                                                   // Имя расширения
+	Title              string            `protobuf:"bytes,10010,opt,name=title,proto3" json:"title,omitempty"`                                                                                           // Название расширения
+	Description        string            `protobuf:"bytes,10020,opt,name=description,proto3" json:"description,omitempty"`                                                                               // Описание расширения
+	Version            string            `protobuf:"bytes,10100,opt,name=version,proto3" json:"version,omitempty"`                                                                                       // Версия расширения
+	VersionDescription string            `protobuf:"bytes,10110,opt,name=version_description,json=versionDescription,proto3" json:"version_description,omitempty"`                                       // Описание версии
+	Deps               []string          `protobuf:"bytes,10200,rep,name=deps,proto3" json:"deps,omitempty"`                                                                                             // Зависимости от других расширений
+	Url                string            `protobuf:"bytes,5,opt,name=url,proto3" json:"url,omitempty"`                                                                                                   // Адрес для внутри сети, коммуникация между сервисами
+	Metadata           map[string]string `protobuf:"bytes,6,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Метаданные сервиса, смотрите документацию по сервису
+}
+
+func (x *ExtensionDescriptor) Reset() {
+	*x = ExtensionDescriptor{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_manager_service_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ExtensionDescriptor) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExtensionDescriptor) ProtoMessage() {}
+
+func (x *ExtensionDescriptor) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_manager_service_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ExtensionDescriptor.ProtoReflect.Descriptor instead.
+func (*ExtensionDescriptor) Descriptor() ([]byte, []int) {
+	return file_extensions_manager_service_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *ExtensionDescriptor) GetExtension() string {
+	if x != nil {
+		return x.Extension
+	}
+	return ""
+}
+
+func (x *ExtensionDescriptor) GetTitle() string {
+	if x != nil {
+		return x.Title
+	}
+	return ""
+}
+
+func (x *ExtensionDescriptor) GetDescription() string {
+	if x != nil {
+		return x.Description
+	}
+	return ""
+}
+
+func (x *ExtensionDescriptor) GetVersion() string {
+	if x != nil {
+		return x.Version
+	}
+	return ""
+}
+
+func (x *ExtensionDescriptor) GetVersionDescription() string {
+	if x != nil {
+		return x.VersionDescription
+	}
+	return ""
+}
+
+func (x *ExtensionDescriptor) GetDeps() []string {
+	if x != nil {
+		return x.Deps
+	}
+	return nil
+}
+
+func (x *ExtensionDescriptor) GetUrl() string {
+	if x != nil {
+		return x.Url
+	}
+	return ""
+}
+
+func (x *ExtensionDescriptor) GetMetadata() map[string]string {
+	if x != nil {
+		return x.Metadata
+	}
+	return nil
+}
+
+type RegisterExtensionsRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Extensions []*ExtensionDescriptor `protobuf:"bytes,1,rep,name=extensions,proto3" json:"extensions,omitempty"`
+}
+
+func (x *RegisterExtensionsRequest) Reset() {
+	*x = RegisterExtensionsRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_manager_service_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *RegisterExtensionsRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RegisterExtensionsRequest) ProtoMessage() {}
+
+func (x *RegisterExtensionsRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_manager_service_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use RegisterExtensionsRequest.ProtoReflect.Descriptor instead.
+func (*RegisterExtensionsRequest) Descriptor() ([]byte, []int) {
+	return file_extensions_manager_service_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *RegisterExtensionsRequest) GetExtensions() []*ExtensionDescriptor {
+	if x != nil {
+		return x.Extensions
+	}
+	return nil
+}
+
+type RegisterExtensionsResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *RegisterExtensionsResponse) Reset() {
+	*x = RegisterExtensionsResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_manager_service_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *RegisterExtensionsResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RegisterExtensionsResponse) ProtoMessage() {}
+
+func (x *RegisterExtensionsResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_manager_service_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use RegisterExtensionsResponse.ProtoReflect.Descriptor instead.
+func (*RegisterExtensionsResponse) Descriptor() ([]byte, []int) {
+	return file_extensions_manager_service_proto_rawDescGZIP(), []int{4}
+}
+
+type UnregisterExtensionsRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Extensions []*ExtensionDescriptor `protobuf:"bytes,1,rep,name=extensions,proto3" json:"extensions,omitempty"`
+}
+
+func (x *UnregisterExtensionsRequest) Reset() {
+	*x = UnregisterExtensionsRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_manager_service_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *UnregisterExtensionsRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UnregisterExtensionsRequest) ProtoMessage() {}
+
+func (x *UnregisterExtensionsRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_manager_service_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use UnregisterExtensionsRequest.ProtoReflect.Descriptor instead.
+func (*UnregisterExtensionsRequest) Descriptor() ([]byte, []int) {
+	return file_extensions_manager_service_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *UnregisterExtensionsRequest) GetExtensions() []*ExtensionDescriptor {
+	if x != nil {
+		return x.Extensions
+	}
+	return nil
+}
+
+type UnregisterExtensionsResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *UnregisterExtensionsResponse) Reset() {
+	*x = UnregisterExtensionsResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_manager_service_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *UnregisterExtensionsResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UnregisterExtensionsResponse) ProtoMessage() {}
+
+func (x *UnregisterExtensionsResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_manager_service_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use UnregisterExtensionsResponse.ProtoReflect.Descriptor instead.
+func (*UnregisterExtensionsResponse) Descriptor() ([]byte, []int) {
+	return file_extensions_manager_service_proto_rawDescGZIP(), []int{6}
+}
+
+type ListExtensionsFilter struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Extension []string `protobuf:"bytes,1,rep,name=extension,proto3" json:"extension,omitempty"` // Список имен сервисов для получения результатов. Список может содержать регулярные выражения.
+}
+
+func (x *ListExtensionsFilter) Reset() {
+	*x = ListExtensionsFilter{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_manager_service_proto_msgTypes[7]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ListExtensionsFilter) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ListExtensionsFilter) ProtoMessage() {}
+
+func (x *ListExtensionsFilter) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_manager_service_proto_msgTypes[7]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ListExtensionsFilter.ProtoReflect.Descriptor instead.
+func (*ListExtensionsFilter) Descriptor() ([]byte, []int) {
+	return file_extensions_manager_service_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *ListExtensionsFilter) GetExtension() []string {
+	if x != nil {
+		return x.Extension
+	}
+	return nil
+}
+
+type ListExtensionsRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Filter *ListExtensionsFilter `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"`
+}
+
+func (x *ListExtensionsRequest) Reset() {
+	*x = ListExtensionsRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_manager_service_proto_msgTypes[8]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ListExtensionsRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ListExtensionsRequest) ProtoMessage() {}
+
+func (x *ListExtensionsRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_manager_service_proto_msgTypes[8]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ListExtensionsRequest.ProtoReflect.Descriptor instead.
+func (*ListExtensionsRequest) Descriptor() ([]byte, []int) {
+	return file_extensions_manager_service_proto_rawDescGZIP(), []int{8}
+}
+
+func (x *ListExtensionsRequest) GetFilter() *ListExtensionsFilter {
+	if x != nil {
+		return x.Filter
+	}
+	return nil
+}
+
+type ListExtensionsResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Extensions []*ExtensionDescriptor `protobuf:"bytes,1,rep,name=extensions,proto3" json:"extensions,omitempty"`
+}
+
+func (x *ListExtensionsResponse) Reset() {
+	*x = ListExtensionsResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_manager_service_proto_msgTypes[9]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ListExtensionsResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ListExtensionsResponse) ProtoMessage() {}
+
+func (x *ListExtensionsResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_manager_service_proto_msgTypes[9]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ListExtensionsResponse.ProtoReflect.Descriptor instead.
+func (*ListExtensionsResponse) Descriptor() ([]byte, []int) {
+	return file_extensions_manager_service_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *ListExtensionsResponse) GetExtensions() []*ExtensionDescriptor {
+	if x != nil {
+		return x.Extensions
+	}
+	return nil
+}
+
+type SpaceExtensions struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Extension   string   `protobuf:"bytes,10000,opt,name=extension,proto3" json:"extension,omitempty"`
+	Title       string   `protobuf:"bytes,10010,opt,name=title,proto3" json:"title,omitempty"`
+	Version     string   `protobuf:"bytes,10100,opt,name=version,proto3" json:"version,omitempty"`
+	Deps        []string `protobuf:"bytes,10200,rep,name=deps,proto3" json:"deps,omitempty"`
+	State       State    `protobuf:"varint,10300,opt,name=state,proto3,enum=extensions.State" json:"state,omitempty"`
+	StatusError string   `protobuf:"bytes,10400,opt,name=status_error,json=statusError,proto3" json:"status_error,omitempty"` // Сообщение к ошибке
+	StatusMsg   string   `protobuf:"bytes,10500,opt,name=status_msg,json=statusMsg,proto3" json:"status_msg,omitempty"`       // Сообщение к статусу
+}
+
+func (x *SpaceExtensions) Reset() {
+	*x = SpaceExtensions{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_manager_service_proto_msgTypes[10]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SpaceExtensions) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SpaceExtensions) ProtoMessage() {}
+
+func (x *SpaceExtensions) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_manager_service_proto_msgTypes[10]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SpaceExtensions.ProtoReflect.Descriptor instead.
+func (*SpaceExtensions) Descriptor() ([]byte, []int) {
+	return file_extensions_manager_service_proto_rawDescGZIP(), []int{10}
+}
+
+func (x *SpaceExtensions) GetExtension() string {
+	if x != nil {
+		return x.Extension
+	}
+	return ""
+}
+
+func (x *SpaceExtensions) GetTitle() string {
+	if x != nil {
+		return x.Title
+	}
+	return ""
+}
+
+func (x *SpaceExtensions) GetVersion() string {
+	if x != nil {
+		return x.Version
+	}
+	return ""
+}
+
+func (x *SpaceExtensions) GetDeps() []string {
+	if x != nil {
+		return x.Deps
+	}
+	return nil
+}
+
+func (x *SpaceExtensions) GetState() State {
+	if x != nil {
+		return x.State
+	}
+	return State_UNKNOWN
+}
+
+func (x *SpaceExtensions) GetStatusError() string {
+	if x != nil {
+		return x.StatusError
+	}
+	return ""
+}
+
+func (x *SpaceExtensions) GetStatusMsg() string {
+	if x != nil {
+		return x.StatusMsg
+	}
+	return ""
+}
+
+type GetInstalledExtensionsResponse_Status struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Extension        string `protobuf:"bytes,10100,opt,name=extension,proto3" json:"extension,omitempty"` // Имя расширения
+	Title            string `protobuf:"bytes,10110,opt,name=title,proto3" json:"title,omitempty"`
+	State            State  `protobuf:"varint,10120,opt,name=state,proto3,enum=extensions.State" json:"state,omitempty"`
+	Msg              string `protobuf:"bytes,10200,opt,name=msg,proto3" json:"msg,omitempty"`                                                   // Сообщение
+	Error            string `protobuf:"bytes,10300,opt,name=error,proto3" json:"error,omitempty"`                                               // Ошибка (state == ERROR)
+	NotFound         bool   `protobuf:"varint,10350,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`                        // Расширение не найдено
+	Installed        bool   `protobuf:"varint,10400,opt,name=installed,proto3" json:"installed,omitempty"`                                      // Расширение установлено
+	UpdateAvailable  bool   `protobuf:"varint,10500,opt,name=update_available,json=updateAvailable,proto3" json:"update_available,omitempty"`   // Доступно обновление
+	InstalledVersion string `protobuf:"bytes,10510,opt,name=installed_version,json=installedVersion,proto3" json:"installed_version,omitempty"` // Установленная версия расширения
+	AvailableVersion string `protobuf:"bytes,10520,opt,name=available_version,json=availableVersion,proto3" json:"available_version,omitempty"` // Доступная версия расширения
+}
+
+func (x *GetInstalledExtensionsResponse_Status) Reset() {
+	*x = GetInstalledExtensionsResponse_Status{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_extensions_manager_service_proto_msgTypes[11]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetInstalledExtensionsResponse_Status) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetInstalledExtensionsResponse_Status) ProtoMessage() {}
+
+func (x *GetInstalledExtensionsResponse_Status) ProtoReflect() protoreflect.Message {
+	mi := &file_extensions_manager_service_proto_msgTypes[11]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetInstalledExtensionsResponse_Status.ProtoReflect.Descriptor instead.
+func (*GetInstalledExtensionsResponse_Status) Descriptor() ([]byte, []int) {
+	return file_extensions_manager_service_proto_rawDescGZIP(), []int{1, 0}
+}
+
+func (x *GetInstalledExtensionsResponse_Status) GetExtension() string {
+	if x != nil {
+		return x.Extension
+	}
+	return ""
+}
+
+func (x *GetInstalledExtensionsResponse_Status) GetTitle() string {
+	if x != nil {
+		return x.Title
+	}
+	return ""
+}
+
+func (x *GetInstalledExtensionsResponse_Status) GetState() State {
+	if x != nil {
+		return x.State
+	}
+	return State_UNKNOWN
+}
+
+func (x *GetInstalledExtensionsResponse_Status) GetMsg() string {
+	if x != nil {
+		return x.Msg
+	}
+	return ""
+}
+
+func (x *GetInstalledExtensionsResponse_Status) GetError() string {
+	if x != nil {
+		return x.Error
+	}
+	return ""
+}
+
+func (x *GetInstalledExtensionsResponse_Status) GetNotFound() bool {
+	if x != nil {
+		return x.NotFound
+	}
+	return false
+}
+
+func (x *GetInstalledExtensionsResponse_Status) GetInstalled() bool {
+	if x != nil {
+		return x.Installed
+	}
+	return false
+}
+
+func (x *GetInstalledExtensionsResponse_Status) GetUpdateAvailable() bool {
+	if x != nil {
+		return x.UpdateAvailable
+	}
+	return false
+}
+
+func (x *GetInstalledExtensionsResponse_Status) GetInstalledVersion() string {
+	if x != nil {
+		return x.InstalledVersion
+	}
+	return ""
+}
+
+func (x *GetInstalledExtensionsResponse_Status) GetAvailableVersion() string {
+	if x != nil {
+		return x.AvailableVersion
+	}
+	return ""
+}
+
+var File_extensions_manager_service_proto protoreflect.FileDescriptor
+
+var file_extensions_manager_service_proto_rawDesc = []byte{
+	0x0a, 0x20, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x6d, 0x61, 0x6e,
+	0x61, 0x67, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x12, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x71,
+	0x0a, 0x1d, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x45, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+	0x1e, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20,
+	0x03, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12,
+	0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e,
+	0x76, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49,
+	0x64, 0x22, 0xc5, 0x03, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c,
+	0x65, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x73, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x45, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a,
+	0xd7, 0x02, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, 0x09, 0x65, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0xf4, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
+	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x05, 0x74, 0x69, 0x74,
+	0x6c, 0x65, 0x18, 0xfe, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65,
+	0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x88, 0x4f, 0x20, 0x01, 0x28, 0x0e,
+	0x32, 0x11, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x74,
+	0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x11, 0x0a, 0x03, 0x6d, 0x73,
+	0x67, 0x18, 0xd8, 0x4f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x15, 0x0a,
+	0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xbc, 0x50, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65,
+	0x72, 0x72, 0x6f, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e,
+	0x64, 0x18, 0xee, 0x50, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75,
+	0x6e, 0x64, 0x12, 0x1d, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x18,
+	0xa0, 0x51, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65,
+	0x64, 0x12, 0x2a, 0x0a, 0x10, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x76, 0x61, 0x69,
+	0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x84, 0x52, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x75, 0x70,
+	0x64, 0x61, 0x74, 0x65, 0x41, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2c, 0x0a,
+	0x11, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69,
+	0x6f, 0x6e, 0x18, 0x8e, 0x52, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61,
+	0x6c, 0x6c, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x11, 0x61,
+	0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+	0x18, 0x98, 0x52, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
+	0x6c, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xea, 0x02, 0x0a, 0x13, 0x45, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
+	0x72, 0x12, 0x1d, 0x0a, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x90,
+	0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x12, 0x15, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x9a, 0x4e, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x21, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
+	0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xa4, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
+	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x07, 0x76, 0x65,
+	0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0xf4, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65,
+	0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x13, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+	0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xfe, 0x4e, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x12, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73, 0x63,
+	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x13, 0x0a, 0x04, 0x64, 0x65, 0x70, 0x73, 0x18,
+	0xd8, 0x4f, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73, 0x12, 0x10, 0x0a, 0x03,
+	0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x49,
+	0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x2d, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
+	0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
+	0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74,
+	0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
+	0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05,
+	0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c,
+	0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x5c, 0x0a, 0x19, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74,
+	0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
+	0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65,
+	0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
+	0x69, 0x6f, 0x6e, 0x73, 0x22, 0x1c, 0x0a, 0x1a, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
+	0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x22, 0x5e, 0x0a, 0x1b, 0x55, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
+	0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x12, 0x3f, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
+	0x6e, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x73, 0x63,
+	0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
+	0x6e, 0x73, 0x22, 0x1e, 0x0a, 0x1c, 0x55, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
+	0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x22, 0x34, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73,
+	0x69, 0x6f, 0x6e, 0x73, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x65,
+	0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x51, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74,
+	0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x12, 0x38, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x20, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c,
+	0x69, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x46, 0x69, 0x6c,
+	0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x59, 0x0a, 0x16, 0x4c,
+	0x69, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
+	0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x78, 0x74, 0x65,
+	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65,
+	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x0f, 0x53, 0x70, 0x61, 0x63, 0x65,
+	0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1d, 0x0a, 0x09, 0x65, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x90, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
+	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x05, 0x74, 0x69, 0x74,
+	0x6c, 0x65, 0x18, 0x9a, 0x4e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65,
+	0x12, 0x19, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0xf4, 0x4e, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x13, 0x0a, 0x04, 0x64,
+	0x65, 0x70, 0x73, 0x18, 0xd8, 0x4f, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73,
+	0x12, 0x28, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0xbc, 0x50, 0x20, 0x01, 0x28, 0x0e,
+	0x32, 0x11, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x74,
+	0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x74,
+	0x61, 0x74, 0x75, 0x73, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0xa0, 0x51, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1e,
+	0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x6d, 0x73, 0x67, 0x18, 0x84, 0x52, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x4d, 0x73, 0x67, 0x2a, 0x4b,
+	0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f,
+	0x57, 0x4e, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x45,
+	0x44, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45,
+	0x53, 0x53, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x03, 0x12, 0x0b,
+	0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x04, 0x32, 0xbb, 0x03, 0x0a, 0x17,
+	0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72,
+	0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x65, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73,
+	0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x2e,
+	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73,
+	0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x73, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73,
+	0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b,
+	0x0a, 0x14, 0x55, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65,
+	0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
+	0x6f, 0x6e, 0x73, 0x2e, 0x55, 0x6e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+	0x28, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x55, 0x6e, 0x72,
+	0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0e, 0x4c,
+	0x69, 0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x2e,
+	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45,
+	0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x22, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c, 0x69,
+	0x73, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x71, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x73,
+	0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
+	0x12, 0x29, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47, 0x65,
+	0x74, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73,
+	0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x65, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x6e, 0x73, 0x74,
+	0x61, 0x6c, 0x6c, 0x65, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74,
+	0x2e, 0x70, 0x65, 0x72, 0x78, 0x2e, 0x72, 0x75, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2f,
+	0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2d, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
+	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x3b, 0x65, 0x78, 0x74, 0x65, 0x6e,
+	0x73, 0x69, 0x6f, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_extensions_manager_service_proto_rawDescOnce sync.Once
+	file_extensions_manager_service_proto_rawDescData = file_extensions_manager_service_proto_rawDesc
+)
+
+func file_extensions_manager_service_proto_rawDescGZIP() []byte {
+	file_extensions_manager_service_proto_rawDescOnce.Do(func() {
+		file_extensions_manager_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_extensions_manager_service_proto_rawDescData)
+	})
+	return file_extensions_manager_service_proto_rawDescData
+}
+
+var file_extensions_manager_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_extensions_manager_service_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
+var file_extensions_manager_service_proto_goTypes = []interface{}{
+	(State)(0),                                    // 0: extensions.State
+	(*GetInstalledExtensionsRequest)(nil),         // 1: extensions.GetInstalledExtensionsRequest
+	(*GetInstalledExtensionsResponse)(nil),        // 2: extensions.GetInstalledExtensionsResponse
+	(*ExtensionDescriptor)(nil),                   // 3: extensions.ExtensionDescriptor
+	(*RegisterExtensionsRequest)(nil),             // 4: extensions.RegisterExtensionsRequest
+	(*RegisterExtensionsResponse)(nil),            // 5: extensions.RegisterExtensionsResponse
+	(*UnregisterExtensionsRequest)(nil),           // 6: extensions.UnregisterExtensionsRequest
+	(*UnregisterExtensionsResponse)(nil),          // 7: extensions.UnregisterExtensionsResponse
+	(*ListExtensionsFilter)(nil),                  // 8: extensions.ListExtensionsFilter
+	(*ListExtensionsRequest)(nil),                 // 9: extensions.ListExtensionsRequest
+	(*ListExtensionsResponse)(nil),                // 10: extensions.ListExtensionsResponse
+	(*SpaceExtensions)(nil),                       // 11: extensions.SpaceExtensions
+	(*GetInstalledExtensionsResponse_Status)(nil), // 12: extensions.GetInstalledExtensionsResponse.Status
+	nil, // 13: extensions.ExtensionDescriptor.MetadataEntry
+}
+var file_extensions_manager_service_proto_depIdxs = []int32{
+	12, // 0: extensions.GetInstalledExtensionsResponse.status:type_name -> extensions.GetInstalledExtensionsResponse.Status
+	13, // 1: extensions.ExtensionDescriptor.metadata:type_name -> extensions.ExtensionDescriptor.MetadataEntry
+	3,  // 2: extensions.RegisterExtensionsRequest.extensions:type_name -> extensions.ExtensionDescriptor
+	3,  // 3: extensions.UnregisterExtensionsRequest.extensions:type_name -> extensions.ExtensionDescriptor
+	8,  // 4: extensions.ListExtensionsRequest.filter:type_name -> extensions.ListExtensionsFilter
+	3,  // 5: extensions.ListExtensionsResponse.extensions:type_name -> extensions.ExtensionDescriptor
+	0,  // 6: extensions.SpaceExtensions.state:type_name -> extensions.State
+	0,  // 7: extensions.GetInstalledExtensionsResponse.Status.state:type_name -> extensions.State
+	4,  // 8: extensions.ExtensionManagerService.RegisterExtensions:input_type -> extensions.RegisterExtensionsRequest
+	6,  // 9: extensions.ExtensionManagerService.UnregisterExtensions:input_type -> extensions.UnregisterExtensionsRequest
+	9,  // 10: extensions.ExtensionManagerService.ListExtensions:input_type -> extensions.ListExtensionsRequest
+	1,  // 11: extensions.ExtensionManagerService.GetInstalledExtensions:input_type -> extensions.GetInstalledExtensionsRequest
+	5,  // 12: extensions.ExtensionManagerService.RegisterExtensions:output_type -> extensions.RegisterExtensionsResponse
+	7,  // 13: extensions.ExtensionManagerService.UnregisterExtensions:output_type -> extensions.UnregisterExtensionsResponse
+	10, // 14: extensions.ExtensionManagerService.ListExtensions:output_type -> extensions.ListExtensionsResponse
+	2,  // 15: extensions.ExtensionManagerService.GetInstalledExtensions:output_type -> extensions.GetInstalledExtensionsResponse
+	12, // [12:16] is the sub-list for method output_type
+	8,  // [8:12] is the sub-list for method input_type
+	8,  // [8:8] is the sub-list for extension type_name
+	8,  // [8:8] is the sub-list for extension extendee
+	0,  // [0:8] is the sub-list for field type_name
+}
+
+func init() { file_extensions_manager_service_proto_init() }
+func file_extensions_manager_service_proto_init() {
+	if File_extensions_manager_service_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_extensions_manager_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetInstalledExtensionsRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_manager_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetInstalledExtensionsResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_manager_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ExtensionDescriptor); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_manager_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*RegisterExtensionsRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_manager_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*RegisterExtensionsResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_manager_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*UnregisterExtensionsRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_manager_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*UnregisterExtensionsResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_manager_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ListExtensionsFilter); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_manager_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ListExtensionsRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_manager_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ListExtensionsResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_manager_service_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SpaceExtensions); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_extensions_manager_service_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetInstalledExtensionsResponse_Status); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_extensions_manager_service_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   13,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_extensions_manager_service_proto_goTypes,
+		DependencyIndexes: file_extensions_manager_service_proto_depIdxs,
+		EnumInfos:         file_extensions_manager_service_proto_enumTypes,
+		MessageInfos:      file_extensions_manager_service_proto_msgTypes,
+	}.Build()
+	File_extensions_manager_service_proto = out.File
+	file_extensions_manager_service_proto_rawDesc = nil
+	file_extensions_manager_service_proto_goTypes = nil
+	file_extensions_manager_service_proto_depIdxs = nil
+}
diff --git a/proto/extensions/manager_grpc.pb.go b/proto/extensions/manager_service_grpc.pb.go
similarity index 53%
rename from proto/extensions/manager_grpc.pb.go
rename to proto/extensions/manager_service_grpc.pb.go
index aa5a5105bffb683691d866f65127857d5453192c..182bb41d2c7fb287077d947b1b27e07eab2b4f2d 100644
--- a/proto/extensions/manager_grpc.pb.go
+++ b/proto/extensions/manager_service_grpc.pb.go
@@ -28,8 +28,8 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v4.23.4
-// source: extensions/manager.proto
+// - protoc             v4.24.3
+// source: extensions/manager_service.proto
 
 package extensions
 
@@ -46,191 +46,231 @@ import (
 const _ = grpc.SupportPackageIsVersion7
 
 const (
-	ExtensionManager_RegisterExtensions_FullMethodName   = "/extensions.ExtensionManager/RegisterExtensions"
-	ExtensionManager_UnregisterExtensions_FullMethodName = "/extensions.ExtensionManager/UnregisterExtensions"
-	ExtensionManager_ListExtensions_FullMethodName       = "/extensions.ExtensionManager/ListExtensions"
+	ExtensionManagerService_RegisterExtensions_FullMethodName     = "/extensions.ExtensionManagerService/RegisterExtensions"
+	ExtensionManagerService_UnregisterExtensions_FullMethodName   = "/extensions.ExtensionManagerService/UnregisterExtensions"
+	ExtensionManagerService_ListExtensions_FullMethodName         = "/extensions.ExtensionManagerService/ListExtensions"
+	ExtensionManagerService_GetInstalledExtensions_FullMethodName = "/extensions.ExtensionManagerService/GetInstalledExtensions"
 )
 
-// ExtensionManagerClient is the client API for ExtensionManager service.
+// ExtensionManagerServiceClient is the client API for ExtensionManagerService service.
 //
 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
-type ExtensionManagerClient interface {
+type ExtensionManagerServiceClient interface {
 	// ##  Регистрация расширений
 	//
-	// Регистрация\Дерегистрация происходить через сервис менеджера расширений (Extension Manager). В процессе регистрации сервис сообщает
+	// Регистрация\Дерегистрация происходит через сервис менеджера расширений (Extension Manager). В процессе регистрации сервис сообщает
 	// о поддерживаемых сервисом действиях (Actions) и версии сервиса и зависимостях:
-	// 1. Действия (Actions) - перечень действия которые обрабатываются сервисом. Включает в себя как системные действия,
-	// так и пользовательские, которые могут быть использованы в интерфейсе;
+	// ~~1. Действия (Actions) - перечень действия которые обрабатываются сервисом. Включает в себя как системные действия,
+	// так и пользовательские, которые могут быть использованы в интерфейсе;~~
 	// 2. Имя сервиса/расширения - название сервиса внутри системы;
 	// 3. Версия сервиса - сервис сообщает текущую версию сервиса. Контроллер сообщает пользователю о возможности обновления
 	// расширения при смене версии;
 	// 4. Описание версии - содержит информацию об изменениях в последних версиях расширения;
 	// 5. Зависимости - перечень расширений которые необходимы сервису для функционирования. При установке сервиса
-	// все расширения от которых он зависит, будут так же установлены.
+	// все расширения, от которых он зависит, будут также установлены.
 	RegisterExtensions(ctx context.Context, in *RegisterExtensionsRequest, opts ...grpc.CallOption) (*RegisterExtensionsResponse, error)
 	UnregisterExtensions(ctx context.Context, in *UnregisterExtensionsRequest, opts ...grpc.CallOption) (*UnregisterExtensionsResponse, error)
 	// Получить список зарегистрированных сервисов
 	ListExtensions(ctx context.Context, in *ListExtensionsRequest, opts ...grpc.CallOption) (*ListExtensionsResponse, error)
+	// GetInstalledExtensions - получить статус расширения
+	GetInstalledExtensions(ctx context.Context, in *GetInstalledExtensionsRequest, opts ...grpc.CallOption) (*GetInstalledExtensionsResponse, error)
 }
 
-type extensionManagerClient struct {
+type extensionManagerServiceClient struct {
 	cc grpc.ClientConnInterface
 }
 
-func NewExtensionManagerClient(cc grpc.ClientConnInterface) ExtensionManagerClient {
-	return &extensionManagerClient{cc}
+func NewExtensionManagerServiceClient(cc grpc.ClientConnInterface) ExtensionManagerServiceClient {
+	return &extensionManagerServiceClient{cc}
 }
 
-func (c *extensionManagerClient) RegisterExtensions(ctx context.Context, in *RegisterExtensionsRequest, opts ...grpc.CallOption) (*RegisterExtensionsResponse, error) {
+func (c *extensionManagerServiceClient) RegisterExtensions(ctx context.Context, in *RegisterExtensionsRequest, opts ...grpc.CallOption) (*RegisterExtensionsResponse, error) {
 	out := new(RegisterExtensionsResponse)
-	err := c.cc.Invoke(ctx, ExtensionManager_RegisterExtensions_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, ExtensionManagerService_RegisterExtensions_FullMethodName, in, out, opts...)
 	if err != nil {
 		return nil, err
 	}
 	return out, nil
 }
 
-func (c *extensionManagerClient) UnregisterExtensions(ctx context.Context, in *UnregisterExtensionsRequest, opts ...grpc.CallOption) (*UnregisterExtensionsResponse, error) {
+func (c *extensionManagerServiceClient) UnregisterExtensions(ctx context.Context, in *UnregisterExtensionsRequest, opts ...grpc.CallOption) (*UnregisterExtensionsResponse, error) {
 	out := new(UnregisterExtensionsResponse)
-	err := c.cc.Invoke(ctx, ExtensionManager_UnregisterExtensions_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, ExtensionManagerService_UnregisterExtensions_FullMethodName, in, out, opts...)
 	if err != nil {
 		return nil, err
 	}
 	return out, nil
 }
 
-func (c *extensionManagerClient) ListExtensions(ctx context.Context, in *ListExtensionsRequest, opts ...grpc.CallOption) (*ListExtensionsResponse, error) {
+func (c *extensionManagerServiceClient) ListExtensions(ctx context.Context, in *ListExtensionsRequest, opts ...grpc.CallOption) (*ListExtensionsResponse, error) {
 	out := new(ListExtensionsResponse)
-	err := c.cc.Invoke(ctx, ExtensionManager_ListExtensions_FullMethodName, in, out, opts...)
+	err := c.cc.Invoke(ctx, ExtensionManagerService_ListExtensions_FullMethodName, in, out, opts...)
 	if err != nil {
 		return nil, err
 	}
 	return out, nil
 }
 
-// ExtensionManagerServer is the server API for ExtensionManager service.
-// All implementations must embed UnimplementedExtensionManagerServer
+func (c *extensionManagerServiceClient) GetInstalledExtensions(ctx context.Context, in *GetInstalledExtensionsRequest, opts ...grpc.CallOption) (*GetInstalledExtensionsResponse, error) {
+	out := new(GetInstalledExtensionsResponse)
+	err := c.cc.Invoke(ctx, ExtensionManagerService_GetInstalledExtensions_FullMethodName, in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// ExtensionManagerServiceServer is the server API for ExtensionManagerService service.
+// All implementations must embed UnimplementedExtensionManagerServiceServer
 // for forward compatibility
-type ExtensionManagerServer interface {
+type ExtensionManagerServiceServer interface {
 	// ##  Регистрация расширений
 	//
-	// Регистрация\Дерегистрация происходить через сервис менеджера расширений (Extension Manager). В процессе регистрации сервис сообщает
+	// Регистрация\Дерегистрация происходит через сервис менеджера расширений (Extension Manager). В процессе регистрации сервис сообщает
 	// о поддерживаемых сервисом действиях (Actions) и версии сервиса и зависимостях:
-	// 1. Действия (Actions) - перечень действия которые обрабатываются сервисом. Включает в себя как системные действия,
-	// так и пользовательские, которые могут быть использованы в интерфейсе;
+	// ~~1. Действия (Actions) - перечень действия которые обрабатываются сервисом. Включает в себя как системные действия,
+	// так и пользовательские, которые могут быть использованы в интерфейсе;~~
 	// 2. Имя сервиса/расширения - название сервиса внутри системы;
 	// 3. Версия сервиса - сервис сообщает текущую версию сервиса. Контроллер сообщает пользователю о возможности обновления
 	// расширения при смене версии;
 	// 4. Описание версии - содержит информацию об изменениях в последних версиях расширения;
 	// 5. Зависимости - перечень расширений которые необходимы сервису для функционирования. При установке сервиса
-	// все расширения от которых он зависит, будут так же установлены.
+	// все расширения, от которых он зависит, будут также установлены.
 	RegisterExtensions(context.Context, *RegisterExtensionsRequest) (*RegisterExtensionsResponse, error)
 	UnregisterExtensions(context.Context, *UnregisterExtensionsRequest) (*UnregisterExtensionsResponse, error)
 	// Получить список зарегистрированных сервисов
 	ListExtensions(context.Context, *ListExtensionsRequest) (*ListExtensionsResponse, error)
-	mustEmbedUnimplementedExtensionManagerServer()
+	// GetInstalledExtensions - получить статус расширения
+	GetInstalledExtensions(context.Context, *GetInstalledExtensionsRequest) (*GetInstalledExtensionsResponse, error)
+	mustEmbedUnimplementedExtensionManagerServiceServer()
 }
 
-// UnimplementedExtensionManagerServer must be embedded to have forward compatible implementations.
-type UnimplementedExtensionManagerServer struct {
+// UnimplementedExtensionManagerServiceServer must be embedded to have forward compatible implementations.
+type UnimplementedExtensionManagerServiceServer struct {
 }
 
-func (UnimplementedExtensionManagerServer) RegisterExtensions(context.Context, *RegisterExtensionsRequest) (*RegisterExtensionsResponse, error) {
+func (UnimplementedExtensionManagerServiceServer) RegisterExtensions(context.Context, *RegisterExtensionsRequest) (*RegisterExtensionsResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method RegisterExtensions not implemented")
 }
-func (UnimplementedExtensionManagerServer) UnregisterExtensions(context.Context, *UnregisterExtensionsRequest) (*UnregisterExtensionsResponse, error) {
+func (UnimplementedExtensionManagerServiceServer) UnregisterExtensions(context.Context, *UnregisterExtensionsRequest) (*UnregisterExtensionsResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method UnregisterExtensions not implemented")
 }
-func (UnimplementedExtensionManagerServer) ListExtensions(context.Context, *ListExtensionsRequest) (*ListExtensionsResponse, error) {
+func (UnimplementedExtensionManagerServiceServer) ListExtensions(context.Context, *ListExtensionsRequest) (*ListExtensionsResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method ListExtensions not implemented")
 }
-func (UnimplementedExtensionManagerServer) mustEmbedUnimplementedExtensionManagerServer() {}
+func (UnimplementedExtensionManagerServiceServer) GetInstalledExtensions(context.Context, *GetInstalledExtensionsRequest) (*GetInstalledExtensionsResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetInstalledExtensions not implemented")
+}
+func (UnimplementedExtensionManagerServiceServer) mustEmbedUnimplementedExtensionManagerServiceServer() {
+}
 
-// UnsafeExtensionManagerServer may be embedded to opt out of forward compatibility for this service.
-// Use of this interface is not recommended, as added methods to ExtensionManagerServer will
+// UnsafeExtensionManagerServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to ExtensionManagerServiceServer will
 // result in compilation errors.
-type UnsafeExtensionManagerServer interface {
-	mustEmbedUnimplementedExtensionManagerServer()
+type UnsafeExtensionManagerServiceServer interface {
+	mustEmbedUnimplementedExtensionManagerServiceServer()
 }
 
-func RegisterExtensionManagerServer(s grpc.ServiceRegistrar, srv ExtensionManagerServer) {
-	s.RegisterService(&ExtensionManager_ServiceDesc, srv)
+func RegisterExtensionManagerServiceServer(s grpc.ServiceRegistrar, srv ExtensionManagerServiceServer) {
+	s.RegisterService(&ExtensionManagerService_ServiceDesc, srv)
 }
 
-func _ExtensionManager_RegisterExtensions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+func _ExtensionManagerService_RegisterExtensions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(RegisterExtensionsRequest)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(ExtensionManagerServer).RegisterExtensions(ctx, in)
+		return srv.(ExtensionManagerServiceServer).RegisterExtensions(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: ExtensionManager_RegisterExtensions_FullMethodName,
+		FullMethod: ExtensionManagerService_RegisterExtensions_FullMethodName,
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ExtensionManagerServer).RegisterExtensions(ctx, req.(*RegisterExtensionsRequest))
+		return srv.(ExtensionManagerServiceServer).RegisterExtensions(ctx, req.(*RegisterExtensionsRequest))
 	}
 	return interceptor(ctx, in, info, handler)
 }
 
-func _ExtensionManager_UnregisterExtensions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+func _ExtensionManagerService_UnregisterExtensions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(UnregisterExtensionsRequest)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(ExtensionManagerServer).UnregisterExtensions(ctx, in)
+		return srv.(ExtensionManagerServiceServer).UnregisterExtensions(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: ExtensionManager_UnregisterExtensions_FullMethodName,
+		FullMethod: ExtensionManagerService_UnregisterExtensions_FullMethodName,
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ExtensionManagerServer).UnregisterExtensions(ctx, req.(*UnregisterExtensionsRequest))
+		return srv.(ExtensionManagerServiceServer).UnregisterExtensions(ctx, req.(*UnregisterExtensionsRequest))
 	}
 	return interceptor(ctx, in, info, handler)
 }
 
-func _ExtensionManager_ListExtensions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+func _ExtensionManagerService_ListExtensions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(ListExtensionsRequest)
 	if err := dec(in); err != nil {
 		return nil, err
 	}
 	if interceptor == nil {
-		return srv.(ExtensionManagerServer).ListExtensions(ctx, in)
+		return srv.(ExtensionManagerServiceServer).ListExtensions(ctx, in)
 	}
 	info := &grpc.UnaryServerInfo{
 		Server:     srv,
-		FullMethod: ExtensionManager_ListExtensions_FullMethodName,
+		FullMethod: ExtensionManagerService_ListExtensions_FullMethodName,
 	}
 	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
-		return srv.(ExtensionManagerServer).ListExtensions(ctx, req.(*ListExtensionsRequest))
+		return srv.(ExtensionManagerServiceServer).ListExtensions(ctx, req.(*ListExtensionsRequest))
 	}
 	return interceptor(ctx, in, info, handler)
 }
 
-// ExtensionManager_ServiceDesc is the grpc.ServiceDesc for ExtensionManager service.
+func _ExtensionManagerService_GetInstalledExtensions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(GetInstalledExtensionsRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ExtensionManagerServiceServer).GetInstalledExtensions(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: ExtensionManagerService_GetInstalledExtensions_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ExtensionManagerServiceServer).GetInstalledExtensions(ctx, req.(*GetInstalledExtensionsRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+// ExtensionManagerService_ServiceDesc is the grpc.ServiceDesc for ExtensionManagerService service.
 // It's only intended for direct use with grpc.RegisterService,
 // and not to be introspected or modified (even as a copy)
-var ExtensionManager_ServiceDesc = grpc.ServiceDesc{
-	ServiceName: "extensions.ExtensionManager",
-	HandlerType: (*ExtensionManagerServer)(nil),
+var ExtensionManagerService_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "extensions.ExtensionManagerService",
+	HandlerType: (*ExtensionManagerServiceServer)(nil),
 	Methods: []grpc.MethodDesc{
 		{
 			MethodName: "RegisterExtensions",
-			Handler:    _ExtensionManager_RegisterExtensions_Handler,
+			Handler:    _ExtensionManagerService_RegisterExtensions_Handler,
 		},
 		{
 			MethodName: "UnregisterExtensions",
-			Handler:    _ExtensionManager_UnregisterExtensions_Handler,
+			Handler:    _ExtensionManagerService_UnregisterExtensions_Handler,
 		},
 		{
 			MethodName: "ListExtensions",
-			Handler:    _ExtensionManager_ListExtensions_Handler,
+			Handler:    _ExtensionManagerService_ListExtensions_Handler,
+		},
+		{
+			MethodName: "GetInstalledExtensions",
+			Handler:    _ExtensionManagerService_GetInstalledExtensions_Handler,
 		},
 	},
 	Streams:  []grpc.StreamDesc{},
-	Metadata: "extensions/manager.proto",
+	Metadata: "extensions/manager_service.proto",
 }
diff --git a/proto/files/files.pb.go b/proto/files/files.pb.go
index aa4c401790f2198c902b0bf2ab2b04f81c37390e..8f93164f9b2f1957fd62aca0fe29a7cdc62d4b29 100644
--- a/proto/files/files.pb.go
+++ b/proto/files/files.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.29.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: files/files.proto
 
 package files
diff --git a/proto/files/files_grpc.pb.go b/proto/files/files_grpc.pb.go
index ee2444091e3d3fa32ee913d3ea76a3de24588c79..234e28599a99f74e704121aeda9e82a9f37005b4 100644
--- a/proto/files/files_grpc.pb.go
+++ b/proto/files/files_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v3.21.12
+// - protoc             v4.24.3
 // source: files/files.proto
 
 package files
diff --git a/proto/images/images.pb.go b/proto/images/images.pb.go
index 0142bf158096fa9dcb535aabc323e9e405dee60f..4f95cb6da087725f8af05fbca35955d9e02c7608 100644
--- a/proto/images/images.pb.go
+++ b/proto/images/images.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.29.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: images/images.proto
 
 package images
diff --git a/proto/images/images_grpc.pb.go b/proto/images/images_grpc.pb.go
index 94d3cef36b1c51b7b0256d54a4fc26936cc9fcec..272c6580743f491a78834708bdcacfef2a166f86 100644
--- a/proto/images/images_grpc.pb.go
+++ b/proto/images/images_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v3.21.12
+// - protoc             v4.24.3
 // source: images/images.proto
 
 package images
diff --git a/proto/invitations/invitations.pb.go b/proto/invitations/invitations.pb.go
index 7ebe5e43aa3cd9463ba7c00c3e60fbdafc262db8..1e9fd729c0ffba1903d60f70ba8f89041684b11d 100644
--- a/proto/invitations/invitations.pb.go
+++ b/proto/invitations/invitations.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.29.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: invitations/invitations.proto
 
 package invitations
diff --git a/proto/invitations/invitations_grpc.pb.go b/proto/invitations/invitations_grpc.pb.go
index cb7a47924a439fae10ca3dbc8ae37951e9b8a09b..d5137fec4deee51fb509d7ee0ba9e0c235f57668 100644
--- a/proto/invitations/invitations_grpc.pb.go
+++ b/proto/invitations/invitations_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v3.21.12
+// - protoc             v4.24.3
 // source: invitations/invitations.proto
 
 package invitations
diff --git a/proto/items/items.pb.go b/proto/items/items.pb.go
index c334f0d43f428774856cfddcc36a59a4b834a91b..4ad1bdd4e99c920d83993fb414cd8137116cf205 100644
--- a/proto/items/items.pb.go
+++ b/proto/items/items.pb.go
@@ -8,8 +8,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.30.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: items/items.proto
 
 package items
@@ -3399,526 +3399,527 @@ var file_items_items_proto_rawDesc = []byte{
 	0x6d, 0x73, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 	0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
 	0x13, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x22, 0x37, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07,
-	0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d,
-	0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x3b, 0x0a, 0x0b,
-	0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x06, 0x65,
-	0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x6f,
-	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x45, 0x72, 0x72, 0x6f,
-	0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x3f, 0x0a, 0x0f, 0x56, 0x61, 0x6c,
-	0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x06,
-	0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63,
-	0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x45, 0x72, 0x72,
-	0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x41, 0x0a, 0x11, 0x4d, 0x6f,
-	0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12,
+	0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x12, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x65, 0x72, 0x72,
+	0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+	0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
+	0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+	0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63,
+	0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x37, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72,
+	0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69,
+	0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64,
+	0x22, 0x3b, 0x0a, 0x0b, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12,
 	0x2c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
 	0x14, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e,
-	0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x97, 0x01,
-	0x0a, 0x0b, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a,
-	0x04, 0x65, 0x64, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x65, 0x64, 0x69,
-	0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x08, 0x52, 0x07, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70,
-	0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x75,
-	0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x66, 0x74, 0x5f, 0x64, 0x65,
-	0x6c, 0x65, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x6f, 0x66, 0x74,
-	0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x64,
-	0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x68, 0x61, 0x72,
-	0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x22, 0xe9, 0x06, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d,
-	0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,
-	0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65,
-	0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76,
-	0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
-	0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65,
-	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
-	0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
-	0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x53, 0x74, 0x61, 0x74,
-	0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x40, 0x0a, 0x0e, 0x63, 0x72, 0x65, 0x61,
-	0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x76, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b,
-	0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
-	0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x63, 0x72,
-	0x65, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x76, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72,
-	0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
-	0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65,
-	0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
-	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
-	0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74,
-	0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f,
-	0x62, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
-	0x64, 0x42, 0x79, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61,
-	0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
-	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
-	0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x2b,
-	0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67,
-	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53,
-	0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x49, 0x0a, 0x0c, 0x74,
-	0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28,
-	0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d,
-	0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69,
-	0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c,
-	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69,
-	0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x76,
-	0x69, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
-	0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x12,
-	0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08,
-	0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x69, 0x64,
-	0x64, 0x65, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65,
-	0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x15, 0x20,
-	0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x3c, 0x0a,
-	0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x16, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65,
-	0x6d, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0b,
-	0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x58, 0x0a, 0x11, 0x54,
-	0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
-	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
-	0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
-	0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3c, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x09,
-	0x0a, 0x05, 0x44, 0x52, 0x41, 0x46, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x50, 0x55, 0x42,
-	0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x48, 0x41, 0x4e,
-	0x47, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x52, 0x43, 0x48, 0x49, 0x56, 0x45,
-	0x44, 0x10, 0x03, 0x22, 0x7d, 0x0a, 0x0b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x43, 0x72, 0x65, 0x61,
-	0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a,
-	0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65,
-	0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69,
-	0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c,
-	0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x74, 0x65,
-	0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d,
-	0x49, 0x64, 0x22, 0x7d, 0x0a, 0x0b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74,
-	0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06,
-	0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e,
-	0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f,
-	0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c,
-	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x74, 0x65, 0x6d,
-	0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49,
-	0x64, 0x22, 0x7e, 0x0a, 0x0c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73,
-	0x68, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06,
-	0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e,
-	0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f,
-	0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c,
-	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x74, 0x65, 0x6d,
-	0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49,
-	0x64, 0x22, 0x80, 0x01, 0x0a, 0x0e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55, 0x6e, 0x70, 0x75, 0x62,
-	0x6c, 0x69, 0x73, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12,
-	0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x3f, 0x0a,
+	0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72,
+	0x12, 0x2c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x14, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73,
+	0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x41,
+	0x0a, 0x11, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72,
+	0x72, 0x6f, 0x72, 0x12, 0x2c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74,
+	0x65, 0x6d, 0x73, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72,
+	0x73, 0x22, 0x97, 0x01, 0x0a, 0x0b, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+	0x73, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x64, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x04, 0x65, 0x64, 0x69, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12,
+	0x18, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08,
+	0x52, 0x07, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x66,
+	0x74, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
+	0x73, 0x6f, 0x66, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x61,
+	0x72, 0x64, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x0a, 0x68, 0x61, 0x72, 0x64, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x22, 0xe9, 0x06, 0x0a, 0x04,
+	0x49, 0x74, 0x65, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12,
+	0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
 	0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63,
-	0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63,
-	0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69,
-	0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x74,
-	0x65, 0x6d, 0x49, 0x64, 0x22, 0x7d, 0x0a, 0x0b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x44, 0x65, 0x6c,
-	0x65, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15,
-	0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
-	0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
-	0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f,
-	0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x74,
-	0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x74, 0x65,
-	0x6d, 0x49, 0x64, 0x22, 0x4a, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a,
-	0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a,
-	0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f,
-	0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x04, 0x64, 0x61, 0x74,
-	0x61, 0x12, 0x0c, 0x0a, 0x01, 0x71, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x01, 0x71, 0x22,
-	0x32, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x73,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74,
-	0x74, 0x72, 0x73, 0x22, 0xa6, 0x01, 0x0a, 0x0b, 0x46, 0x69, 0x6e, 0x64, 0x4f, 0x70, 0x74, 0x69,
-	0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x69,
-	0x6e, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07,
-	0x72, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72,
-	0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e,
-	0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x12, 0x1c,
-	0x0a, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28,
-	0x08, 0x52, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x22, 0x32, 0x0a, 0x0d,
-	0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a,
-	0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x73,
-	0x22, 0x32, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64,
-	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
-	0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61,
-	0x6c, 0x65, 0x49, 0x64, 0x22, 0x48, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63,
+	0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x05, 0x73,
+	0x74, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x6e,
+	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x2e,
+	0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x40, 0x0a, 0x0e,
+	0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x76, 0x5f, 0x61, 0x74, 0x18, 0x06,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
+	0x52, 0x0c, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x76, 0x41, 0x74, 0x12, 0x1d,
+	0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x07, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x39, 0x0a,
+	0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63,
+	0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61,
+	0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x70,
+	0x64, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74,
+	0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
+	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
+	0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64,
+	0x41, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+	0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12,
+	0x49, 0x0a, 0x0c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
+	0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73,
+	0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x74, 0x72,
+	0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65,
+	0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x0a, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6c,
+	0x6f, 0x63, 0x61, 0x6c, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6c, 0x6f, 0x63,
+	0x61, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x13,
+	0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x16, 0x0a,
+	0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68,
+	0x69, 0x64, 0x64, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
+	0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
+	0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
+	0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+	0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
+	0x6e, 0x73, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a,
+	0x58, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45,
+	0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x05,
+	0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3c, 0x0a, 0x05, 0x53, 0x74, 0x61,
+	0x74, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x52, 0x41, 0x46, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a,
+	0x09, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07,
+	0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x52, 0x43,
+	0x48, 0x49, 0x56, 0x45, 0x44, 0x10, 0x03, 0x22, 0x7d, 0x0a, 0x0b, 0x45, 0x76, 0x65, 0x6e, 0x74,
+	0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f,
+	0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49,
+	0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c,
+	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a,
+	0x07, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
+	0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x22, 0x7d, 0x0a, 0x0b, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55,
+	0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69,
+	0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64,
+	0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65,
+	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
+	0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07,
+	0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69,
+	0x74, 0x65, 0x6d, 0x49, 0x64, 0x22, 0x7e, 0x0a, 0x0c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x75,
+	0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69,
+	0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64,
+	0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65,
+	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
+	0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07,
+	0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69,
+	0x74, 0x65, 0x6d, 0x49, 0x64, 0x22, 0x80, 0x01, 0x0a, 0x0e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x55,
+	0x6e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63,
+	0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63,
+	0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f,
+	0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12,
+	0x17, 0x0a, 0x07, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x22, 0x7d, 0x0a, 0x0b, 0x45, 0x76, 0x65, 0x6e,
+	0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65,
+	0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65,
+	0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c,
+	0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17,
+	0x0a, 0x07, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x22, 0x4a, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65,
+	0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x02, 0x69,
+	0x64, 0x12, 0x22, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
+	0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52,
+	0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x0c, 0x0a, 0x01, 0x71, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x01, 0x71, 0x22, 0x32, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x61,
+	0x74, 0x74, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61,
+	0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x73, 0x22, 0xa6, 0x01, 0x0a, 0x0b, 0x46, 0x69, 0x6e, 0x64,
+	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
+	0x6e, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65,
+	0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64,
+	0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28,
+	0x08, 0x52, 0x07, 0x72, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x69,
+	0x64, 0x64, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x69, 0x64, 0x64,
+	0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18,
+	0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73,
+	0x22, 0x32, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72,
+	0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41,
+	0x74, 0x74, 0x72, 0x73, 0x22, 0x32, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69,
+	0x73, 0x68, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6c,
+	0x6f, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
+	0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x49, 0x64, 0x22, 0x48, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65,
+	0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x64,
+	0x61, 0x74, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05,
+	0x65, 0x72, 0x61, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x65, 0x72, 0x61,
+	0x73, 0x65, 0x22, 0x34, 0x0a, 0x0f, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x70,
 	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f,
 	0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x70, 0x64,
-	0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x61, 0x73,
-	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x65, 0x72, 0x61, 0x73, 0x65, 0x22, 0x34,
-	0x0a, 0x0f, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x73, 0x22, 0x33, 0x0a, 0x0e, 0x50, 0x75, 0x62, 0x6c,
+	0x69, 0x73, 0x68, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70,
+	0x64, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
+	0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x73, 0x22, 0x35, 0x0a,
+	0x10, 0x55, 0x6e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
 	0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72,
 	0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41,
-	0x74, 0x74, 0x72, 0x73, 0x22, 0x33, 0x0a, 0x0e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4f,
-	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
-	0x5f, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x70,
-	0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x73, 0x22, 0x35, 0x0a, 0x10, 0x55, 0x6e, 0x70,
-	0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a,
-	0x0c, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x72, 0x73, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x08, 0x52, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x72, 0x73,
-	0x22, 0xb2, 0x01, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68,
-	0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x6f, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6d,
-	0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52,
-	0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61,
-	0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63,
-	0x61, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72,
-	0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x67, 0x75, 0x6c, 0x61, 0x72, 0x12,
-	0x16, 0x0a, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
-	0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x65, 0x6d, 0x70, 0x6c,
-	0x61, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x74, 0x65, 0x6d, 0x70,
-	0x6c, 0x61, 0x74, 0x65, 0x73, 0x22, 0x44, 0x0a, 0x13, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x72, 0x63,
-	0x68, 0x69, 0x76, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, 0x07,
-	0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e,
-	0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x45, 0x0a, 0x14, 0x4c,
-	0x69, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x4f, 0x70, 0x74, 0x69,
-	0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x69,
-	0x6e, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x10, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65,
-	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64,
-	0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
-	0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74,
-	0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45,
-	0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x39, 0x0a, 0x0b,
-	0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
-	0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,
-	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61,
-	0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa4, 0x01, 0x0a, 0x19, 0x41, 0x67, 0x67, 0x72,
-	0x65, 0x67, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x70,
-	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4c, 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18,
-	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
-	0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x50,
-	0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
-	0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x65,
-	0x6c, 0x64, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74,
-	0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x70,
-	0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
-	0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e,
-	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74,
-	0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x36, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69,
-	0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
-	0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
-	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x22, 0x3f, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x12, 0x2d, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74,
-	0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
-	0x64, 0x22, 0x3c, 0x0a, 0x11, 0x49, 0x6e, 0x74, 0x72, 0x6f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69,
-	0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22,
-	0xab, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x74, 0x72, 0x6f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69,
-	0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12,
-	0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x54, 0x0a, 0x11, 0x76, 0x61, 0x6c, 0x69, 0x64,
-	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03,
-	0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x72, 0x72, 0x6f,
-	0x72, 0x2e, 0x42, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x69, 0x65,
-	0x6c, 0x64, 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x76, 0x61, 0x6c,
-	0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x7c, 0x0a,
-	0x0a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73,
-	0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73,
-	0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a,
-	0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
-	0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x22, 0x36, 0x0a, 0x0b, 0x47,
-	0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74,
-	0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
-	0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69,
-	0x74, 0x65, 0x6d, 0x22, 0xc9, 0x01, 0x0a, 0x0b, 0x46, 0x69, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15,
-	0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
-	0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
-	0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f,
-	0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x06, 0x66, 0x69,
-	0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e,
-	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65,
-	0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x07, 0x6f, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6e,
-	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4f,
+	0x74, 0x74, 0x72, 0x73, 0x22, 0xb2, 0x01, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x75, 0x62,
+	0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a,
+	0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13,
+	0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1b, 0x0a, 0x09,
+	0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x67,
+	0x75, 0x6c, 0x61, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x67, 0x75,
+	0x6c, 0x61, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x18, 0x05, 0x20,
+	0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x74,
+	0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09,
+	0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x22, 0x44, 0x0a, 0x13, 0x46, 0x69, 0x6e,
+	0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x12, 0x2d, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4f,
 	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22,
-	0x4f, 0x0a, 0x0c, 0x46, 0x69, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
-	0x29, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13,
-	0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49,
-	0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f,
-	0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c,
-	0x22, 0x70, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
-	0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e,
-	0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x36, 0x0a, 0x07, 0x6f, 0x70,
-	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f,
-	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61,
-	0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x22, 0x70, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75,
+	0x45, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73,
+	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
+	0x6e, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x10, 0x41, 0x67, 0x67, 0x72, 0x65,
+	0x67, 0x61, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x06, 0x66,
+	0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f,
+	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x67, 0x72,
+	0x65, 0x67, 0x61, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x46, 0x69, 0x65,
+	0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73,
+	0x1a, 0x39, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
+	0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
+	0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa4, 0x01, 0x0a, 0x19,
+	0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68,
+	0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x4c, 0x0a, 0x06, 0x66, 0x69, 0x65,
+	0x6c, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
+	0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67,
+	0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
+	0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x46, 0x69, 0x65, 0x6c, 0x64,
+	0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
+	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
+	0x38, 0x01, 0x22, 0x70, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75,
 	0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28,
 	0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d,
 	0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x36, 0x0a, 0x07,
 	0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
-	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x44, 0x65,
-	0x6c, 0x65, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x73, 0x22, 0x74, 0x0a, 0x0f, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
-	0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d,
-	0x12, 0x38, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d,
-	0x73, 0x2e, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
-	0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x72, 0x0a, 0x0e, 0x50, 0x75,
-	0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04,
-	0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e,
-	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52,
-	0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x37, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
-	0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4f, 0x70,
-	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x76,
-	0x0a, 0x10, 0x55, 0x6e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
-	0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73,
-	0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x39, 0x0a, 0x07, 0x6f,
-	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63,
-	0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x55, 0x6e, 0x70,
-	0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f,
-	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xc3, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x75,
-	0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19,
-	0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76,
-	0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64,
-	0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69,
-	0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
-	0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64,
-	0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x12, 0x3c,
-	0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32,
-	0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e,
-	0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69,
-	0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x3f, 0x0a, 0x14,
-	0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65,
-	0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xdb, 0x01,
-	0x0a, 0x14, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f,
-	0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49,
-	0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c,
-	0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2d, 0x0a,
-	0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e,
-	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69,
-	0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x3d, 0x0a, 0x07,
-	0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e,
-	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69,
-	0x6e, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x58, 0x0a, 0x15, 0x46,
-	0x69, 0x6e, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20,
-	0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74,
-	0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12,
-	0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05,
-	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0xd3, 0x01, 0x0a, 0x10, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67,
-	0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70,
-	0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70,
-	0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d,
-	0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49,
-	0x64, 0x12, 0x2d, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d,
-	0x73, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72,
-	0x12, 0x39, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d,
-	0x73, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x44, 0x0a, 0x11, 0x41,
-	0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x12, 0x2f, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
-	0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
-	0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c,
-	0x74, 0x22, 0xe5, 0x01, 0x0a, 0x19, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x50,
-	0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
-	0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e,
-	0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49,
-	0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
-	0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63,
-	0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72,
-	0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
-	0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66,
-	0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
-	0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65,
-	0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x4d, 0x0a, 0x1a, 0x41, 0x67, 0x67,
-	0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c,
-	0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
-	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74,
-	0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xa5, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74,
-	0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x43, 0x72,
+	0x65, 0x61, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x73, 0x22, 0x3f, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
+	0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+	0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x07, 0x63, 0x72,
+	0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x3c, 0x0a, 0x11, 0x49, 0x6e, 0x74, 0x72, 0x6f, 0x73, 0x70,
+	0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74,
+	0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
+	0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69,
+	0x74, 0x65, 0x6d, 0x22, 0xab, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x74, 0x72, 0x6f, 0x73, 0x70, 0x65,
+	0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74,
+	0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
+	0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69,
+	0x74, 0x65, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x54, 0x0a, 0x11, 0x76,
+	0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73,
+	0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
+	0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x42, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
+	0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72,
+	0x73, 0x22, 0x7c, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
 	0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
 	0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e,
 	0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49,
 	0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
 	0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63,
 	0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69,
-	0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x12,
-	0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64,
-	0x22, 0x3e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
-	0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d,
-	0x22, 0xc5, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f,
-	0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61,
-	0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61,
-	0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63,
-	0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64,
-	0x12, 0x17, 0x0a, 0x07, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x12, 0x3d, 0x0a, 0x07, 0x6f, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x6e,
-	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52,
-	0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52,
-	0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x42, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74,
-	0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x12, 0x29, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
-	0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73,
-	0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x39, 0x0a, 0x0e,
-	0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27,
+	0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x22,
+	0x36, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27,
 	0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63,
 	0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65,
-	0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x3b, 0x0a, 0x10, 0x55, 0x6e, 0x61, 0x72, 0x63,
-	0x68, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x69,
+	0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xc9, 0x01, 0x0a, 0x0b, 0x46, 0x69, 0x6e, 0x64,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65,
+	0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65,
+	0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c,
+	0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2d,
+	0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15,
+	0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46,
+	0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x34, 0x0a,
+	0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
+	0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46,
+	0x69, 0x6e, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x22, 0x4f, 0x0a, 0x0c, 0x46, 0x69, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65,
+	0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x14,
+	0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74,
+	0x6f, 0x74, 0x61, 0x6c, 0x22, 0x70, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74,
+	0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x36,
+	0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e,
+	0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x70, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
+	0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d,
+	0x12, 0x36, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d,
+	0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52,
+	0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x74, 0x0a, 0x0f, 0x55, 0x6e, 0x64, 0x65,
+	0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x69,
 	0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
 	0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04,
-	0x69, 0x74, 0x65, 0x6d, 0x22, 0xd9, 0x01, 0x0a, 0x13, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x72, 0x63,
-	0x68, 0x69, 0x76, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08,
-	0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
-	0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69,
-	0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23,
-	0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18,
-	0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f,
-	0x6e, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74,
-	0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74,
-	0x65, 0x72, 0x12, 0x3c, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20,
+	0x69, 0x74, 0x65, 0x6d, 0x12, 0x38, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
+	0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4f, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x72,
+	0x0a, 0x0e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13,
+	0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49,
+	0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x37, 0x0a, 0x07, 0x6f, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x63, 0x6f, 0x6e,
+	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69,
+	0x73, 0x68, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x22, 0x76, 0x0a, 0x10, 0x55, 0x6e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69,
+	0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12,
+	0x39, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73,
+	0x2e, 0x55, 0x6e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xc3, 0x01, 0x0a, 0x13, 0x47,
+	0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a,
+	0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65,
+	0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69,
+	0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c,
+	0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x74, 0x65,
+	0x6d, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d,
+	0x49, 0x64, 0x12, 0x3c, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20,
 	0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74,
-	0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64,
+	0x65, 0x6d, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64,
 	0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x22, 0x57, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64,
+	0x22, 0x3f, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+	0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65,
+	0x6d, 0x22, 0xdb, 0x01, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73,
+	0x68, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70,
+	0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70,
+	0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d,
+	0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49,
+	0x64, 0x12, 0x2d, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d,
+	0x73, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72,
+	0x12, 0x3d, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d,
+	0x73, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22,
+	0x58, 0x0a, 0x15, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64,
 	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d,
 	0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
 	0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74,
 	0x65, 0x6d, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x05, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x32, 0x93, 0x0b, 0x0a, 0x05, 0x49, 0x74,
-	0x65, 0x6d, 0x73, 0x12, 0x47, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x1c, 0x2e,
-	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x43, 0x72,
-	0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x63, 0x6f,
-	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61,
-	0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x0a,
-	0x49, 0x6e, 0x74, 0x72, 0x6f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x12, 0x20, 0x2e, 0x63, 0x6f, 0x6e,
-	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x6e, 0x74, 0x72, 0x6f,
-	0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x63,
-	0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x6e, 0x74,
-	0x72, 0x6f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
-	0x00, 0x12, 0x3e, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x19, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
-	0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74,
-	0x65, 0x6d, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
-	0x00, 0x12, 0x41, 0x0a, 0x04, 0x46, 0x69, 0x6e, 0x64, 0x12, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
+	0x28, 0x05, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x22, 0xd3, 0x01, 0x0a, 0x10, 0x41, 0x67,
+	0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19,
+	0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76,
+	0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64,
+	0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69,
+	0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
+	0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
+	0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69,
+	0x6c, 0x74, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
+	0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x4f,
+	0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22,
+	0x44, 0x0a, 0x11, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x72,
+	0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xe5, 0x01, 0x0a, 0x19, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67,
+	0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15,
+	0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
+	0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
+	0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f,
+	0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x06, 0x66, 0x69,
+	0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e,
+	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65,
+	0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x07, 0x6f, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x63, 0x6f, 0x6e,
+	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65,
+	0x67, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x4f, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x4d, 0x0a,
+	0x1a, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73,
+	0x68, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x72,
+	0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f,
+	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74,
+	0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0xa5, 0x01, 0x0a,
+	0x12, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15,
+	0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
+	0x65, 0x6e, 0x76, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74,
+	0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f,
+	0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x74,
+	0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x74, 0x65,
+	0x6d, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
+	0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69,
+	0x6f, 0x6e, 0x49, 0x64, 0x22, 0x3e, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73,
+	0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x04, 0x69,
+	0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
+	0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04,
+	0x69, 0x74, 0x65, 0x6d, 0x22, 0xc5, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x76,
+	0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a,
+	0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65, 0x6e, 0x76, 0x5f,
+	0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76, 0x49, 0x64, 0x12,
+	0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69,
+	0x6f, 0x6e, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x12, 0x3d, 0x0a,
+	0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23,
+	0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x4c,
+	0x69, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x4f, 0x70, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x42, 0x0a, 0x15,
+	0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69,
+	0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73,
+	0x22, 0x39, 0x0a, 0x0e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x13, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73,
+	0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x3b, 0x0a, 0x10, 0x55,
+	0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+	0x27, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e,
+	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74,
+	0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0xd9, 0x01, 0x0a, 0x13, 0x46, 0x69, 0x6e,
+	0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x65,
+	0x6e, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6e, 0x76,
+	0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+	0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x6c, 0x6c, 0x65,
+	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65,
+	0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+	0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06,
+	0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x07, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+	0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x72, 0x63, 0x68,
+	0x69, 0x76, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x07, 0x6f, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x73, 0x22, 0x57, 0x0a, 0x14, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x72, 0x63, 0x68,
+	0x69, 0x76, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x05,
+	0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x6f,
+	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49, 0x74, 0x65, 0x6d,
+	0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x32, 0x93, 0x0b,
+	0x0a, 0x05, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x47, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74,
+	0x65, 0x12, 0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d,
+	0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+	0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e,
+	0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
+	0x12, 0x53, 0x0a, 0x0a, 0x49, 0x6e, 0x74, 0x72, 0x6f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x12, 0x20,
+	0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x49,
+	0x6e, 0x74, 0x72, 0x6f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73,
+	0x2e, 0x49, 0x6e, 0x74, 0x72, 0x6f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x19, 0x2e, 0x63,
+	0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x47, 0x65, 0x74,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+	0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x04, 0x46, 0x69, 0x6e, 0x64, 0x12, 0x1a, 0x2e,
+	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69,
+	0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
 	0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
-	0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1c,
-	0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x55,
-	0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61,
+	0x74, 0x65, 0x12, 0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65,
+	0x6d, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+	0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x06, 0x44, 0x65,
+	0x6c, 0x65, 0x74, 0x65, 0x12, 0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69,
+	0x74, 0x65, 0x6d, 0x73, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x08,
+	0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
+	0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x74,
+	0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
+	0x22, 0x00, 0x12, 0x42, 0x0a, 0x07, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x1d, 0x2e,
+	0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x50, 0x75,
+	0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67,
 	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45,
-	0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
-	0x12, 0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73,
-	0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16,
-	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
-	0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x08, 0x55, 0x6e, 0x64, 0x65,
-	0x6c, 0x65, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69,
-	0x74, 0x65, 0x6d, 0x73, 0x2e, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71,
+	0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x09, 0x55, 0x6e, 0x70, 0x75, 0x62, 0x6c,
+	0x69, 0x73, 0x68, 0x12, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74,
+	0x65, 0x6d, 0x73, 0x2e, 0x55, 0x6e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71,
 	0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x42,
-	0x0a, 0x07, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
-	0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73,
-	0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
-	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
-	0x22, 0x00, 0x12, 0x46, 0x0a, 0x09, 0x55, 0x6e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12,
-	0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e,
-	0x55, 0x6e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
-	0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0c, 0x47, 0x65,
-	0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x6e,
-	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x75,
-	0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23,
+	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x59,
+	0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x22,
 	0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x47,
-	0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f,
-	0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5c, 0x0a, 0x0d, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x75, 0x62,
-	0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
-	0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69,
-	0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x63, 0x6f,
-	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6e, 0x64,
-	0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x22, 0x00, 0x12, 0x50, 0x0a, 0x09, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65,
-	0x12, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73,
-	0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x1a, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d,
-	0x73, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
-	0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x12, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61,
-	0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x28, 0x2e, 0x63, 0x6f,
-	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x67, 0x72,
-	0x65, 0x67, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
-	0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x50,
-	0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x22, 0x00, 0x12, 0x56, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f,
-	0x6e, 0x12, 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d,
-	0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69,
-	0x74, 0x65, 0x6d, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
-	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5c, 0x0a, 0x0d, 0x4c, 0x69,
-	0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x2e, 0x63, 0x6f,
-	0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74,
-	0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x24, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73,
-	0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x07, 0x41, 0x72, 0x63, 0x68,
-	0x69, 0x76, 0x65, 0x12, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74,
-	0x65, 0x6d, 0x73, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x59, 0x0a, 0x0c,
-	0x46, 0x69, 0x6e, 0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x12, 0x22, 0x2e, 0x63,
-	0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6e,
-	0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73,
-	0x2e, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x09, 0x55, 0x6e, 0x61, 0x72, 0x63,
-	0x68, 0x69, 0x76, 0x65, 0x12, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69,
-	0x74, 0x65, 0x6d, 0x73, 0x2e, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42,
-	0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x2e, 0x70, 0x65, 0x72, 0x78, 0x2e, 0x72, 0x75, 0x2f, 0x70,
-	0x65, 0x72, 0x78, 0x69, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2d, 0x67, 0x6f, 0x2f,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x3b, 0x69, 0x74, 0x65, 0x6d,
-	0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65,
+	0x6d, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5c, 0x0a, 0x0d, 0x46, 0x69, 0x6e,
+	0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x23, 0x2e, 0x63, 0x6f, 0x6e,
+	0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x50,
+	0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+	0x24, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e,
+	0x46, 0x69, 0x6e, 0x64, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x50, 0x0a, 0x09, 0x41, 0x67, 0x67, 0x72, 0x65,
+	0x67, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69,
+	0x74, 0x65, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
+	0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x12, 0x41, 0x67, 0x67,
+	0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12,
+	0x28, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e,
+	0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68,
+	0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x63, 0x6f, 0x6e, 0x74,
+	0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67,
+	0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76,
+	0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e,
+	0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f,
+	0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
+	0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69,
+	0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5c,
+	0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12,
+	0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e,
+	0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69,
+	0x74, 0x65, 0x6d, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f,
+	0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x07,
+	0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x1d, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
+	0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00,
+	0x12, 0x59, 0x0a, 0x0c, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64,
+	0x12, 0x22, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73,
+	0x2e, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x64, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2e, 0x69,
+	0x74, 0x65, 0x6d, 0x73, 0x2e, 0x46, 0x69, 0x6e, 0x64, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65,
+	0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x09, 0x55,
+	0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x65,
+	0x6e, 0x74, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x2e, 0x55, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69,
+	0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
+	0x79, 0x22, 0x00, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x2e, 0x70, 0x65, 0x72, 0x78, 0x2e,
+	0x72, 0x75, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x78, 0x69, 0x73,
+	0x2d, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x3b,
+	0x69, 0x74, 0x65, 0x6d, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/proto/items/items_grpc.pb.go b/proto/items/items_grpc.pb.go
index 7c7d321a9b1a57290a874dc8723679e9b6e755f2..b246b7627a8bbbdaaba51165e9f7b7e936691a11 100644
--- a/proto/items/items_grpc.pb.go
+++ b/proto/items/items_grpc.pb.go
@@ -9,7 +9,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v3.21.12
+// - protoc             v4.24.3
 // source: items/items.proto
 
 package items
diff --git a/proto/locales/locales.pb.go b/proto/locales/locales.pb.go
index b4976bef329f5416bf10bd25f2dc2d8559b15d97..6ed197b9a1d42660f0211daaaba01f7334ff93df 100644
--- a/proto/locales/locales.pb.go
+++ b/proto/locales/locales.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.29.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: locales/locales.proto
 
 package locales
diff --git a/proto/locales/locales_grpc.pb.go b/proto/locales/locales_grpc.pb.go
index 6d2cc59bfb68665cb229a72830e646c7eb0aa30d..d8f5e712b8b06e5646d56020c1e0cb06ae24545f 100644
--- a/proto/locales/locales_grpc.pb.go
+++ b/proto/locales/locales_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v3.21.12
+// - protoc             v4.24.3
 // source: locales/locales.proto
 
 package locales
diff --git a/proto/members/members.pb.go b/proto/members/members.pb.go
index f64cccf98ba400727fa1fa8e621978fb9b2133c2..a939a3f8189f6f100b61a4ad070a3c4fb30fb023 100644
--- a/proto/members/members.pb.go
+++ b/proto/members/members.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.29.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: members/members.proto
 
 package members
diff --git a/proto/members/members_grpc.pb.go b/proto/members/members_grpc.pb.go
index 8e8a103fd6254175c04f60df28c710ea72e3a060..c0216bfe1d7c10c12a4d02d32b5ba6774cfcd59c 100644
--- a/proto/members/members_grpc.pb.go
+++ b/proto/members/members_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v3.21.12
+// - protoc             v4.24.3
 // source: members/members.proto
 
 package members
diff --git a/proto/mocks/ClientsClient.go b/proto/mocks/ClientsClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..3234e72429d2ea922307b4acf9afde5042433a7a
--- /dev/null
+++ b/proto/mocks/ClientsClient.go
@@ -0,0 +1,265 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	clients "git.perx.ru/perxis/perxis-go/proto/clients"
+
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	grpc "google.golang.org/grpc"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// ClientsClient is an autogenerated mock type for the ClientsClient type
+type ClientsClient struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: ctx, in, opts
+func (_m *ClientsClient) Create(ctx context.Context, in *clients.CreateRequest, opts ...grpc.CallOption) (*clients.CreateResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *clients.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.CreateRequest, ...grpc.CallOption) (*clients.CreateResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.CreateRequest, ...grpc.CallOption) *clients.CreateResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*clients.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.CreateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, in, opts
+func (_m *ClientsClient) Delete(ctx context.Context, in *clients.DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.DeleteRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.DeleteRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.DeleteRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Enable provides a mock function with given fields: ctx, in, opts
+func (_m *ClientsClient) Enable(ctx context.Context, in *clients.EnableRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.EnableRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.EnableRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.EnableRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *ClientsClient) Get(ctx context.Context, in *clients.GetRequest, opts ...grpc.CallOption) (*clients.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *clients.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.GetRequest, ...grpc.CallOption) (*clients.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.GetRequest, ...grpc.CallOption) *clients.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*clients.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.GetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetBy provides a mock function with given fields: ctx, in, opts
+func (_m *ClientsClient) GetBy(ctx context.Context, in *clients.GetByRequest, opts ...grpc.CallOption) (*clients.GetByResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *clients.GetByResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.GetByRequest, ...grpc.CallOption) (*clients.GetByResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.GetByRequest, ...grpc.CallOption) *clients.GetByResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*clients.GetByResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.GetByRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: ctx, in, opts
+func (_m *ClientsClient) List(ctx context.Context, in *clients.ListRequest, opts ...grpc.CallOption) (*clients.ListResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *clients.ListResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.ListRequest, ...grpc.CallOption) (*clients.ListResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.ListRequest, ...grpc.CallOption) *clients.ListResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*clients.ListResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.ListRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: ctx, in, opts
+func (_m *ClientsClient) Update(ctx context.Context, in *clients.UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.UpdateRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.UpdateRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.UpdateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewClientsClient creates a new instance of ClientsClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewClientsClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *ClientsClient {
+	mock := &ClientsClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/ClientsServer.go b/proto/mocks/ClientsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..75d6e4f176b69f5c6a03307c588c66d0362e4835
--- /dev/null
+++ b/proto/mocks/ClientsServer.go
@@ -0,0 +1,219 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	clients "git.perx.ru/perxis/perxis-go/proto/clients"
+
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// ClientsServer is an autogenerated mock type for the ClientsServer type
+type ClientsServer struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: _a0, _a1
+func (_m *ClientsServer) Create(_a0 context.Context, _a1 *clients.CreateRequest) (*clients.CreateResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *clients.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.CreateRequest) (*clients.CreateResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.CreateRequest) *clients.CreateResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*clients.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.CreateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: _a0, _a1
+func (_m *ClientsServer) Delete(_a0 context.Context, _a1 *clients.DeleteRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.DeleteRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.DeleteRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.DeleteRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Enable provides a mock function with given fields: _a0, _a1
+func (_m *ClientsServer) Enable(_a0 context.Context, _a1 *clients.EnableRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.EnableRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.EnableRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.EnableRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *ClientsServer) Get(_a0 context.Context, _a1 *clients.GetRequest) (*clients.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *clients.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.GetRequest) (*clients.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.GetRequest) *clients.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*clients.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.GetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetBy provides a mock function with given fields: _a0, _a1
+func (_m *ClientsServer) GetBy(_a0 context.Context, _a1 *clients.GetByRequest) (*clients.GetByResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *clients.GetByResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.GetByRequest) (*clients.GetByResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.GetByRequest) *clients.GetByResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*clients.GetByResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.GetByRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: _a0, _a1
+func (_m *ClientsServer) List(_a0 context.Context, _a1 *clients.ListRequest) (*clients.ListResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *clients.ListResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.ListRequest) (*clients.ListResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.ListRequest) *clients.ListResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*clients.ListResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.ListRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: _a0, _a1
+func (_m *ClientsServer) Update(_a0 context.Context, _a1 *clients.UpdateRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.UpdateRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *clients.UpdateRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *clients.UpdateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedClientsServer provides a mock function with given fields:
+func (_m *ClientsServer) mustEmbedUnimplementedClientsServer() {
+	_m.Called()
+}
+
+// NewClientsServer creates a new instance of ClientsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewClientsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *ClientsServer {
+	mock := &ClientsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/CollaboratorsClient.go b/proto/mocks/CollaboratorsClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..8d9cb07fce907d4a130fe26ec08aa3c266d442ec
--- /dev/null
+++ b/proto/mocks/CollaboratorsClient.go
@@ -0,0 +1,199 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	collaborators "git.perx.ru/perxis/perxis-go/proto/collaborators"
+
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	grpc "google.golang.org/grpc"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// CollaboratorsClient is an autogenerated mock type for the CollaboratorsClient type
+type CollaboratorsClient struct {
+	mock.Mock
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *CollaboratorsClient) Get(ctx context.Context, in *collaborators.GetRequest, opts ...grpc.CallOption) (*collaborators.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *collaborators.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.GetRequest, ...grpc.CallOption) (*collaborators.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.GetRequest, ...grpc.CallOption) *collaborators.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*collaborators.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.GetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListCollaborators provides a mock function with given fields: ctx, in, opts
+func (_m *CollaboratorsClient) ListCollaborators(ctx context.Context, in *collaborators.ListCollaboratorsRequest, opts ...grpc.CallOption) (*collaborators.ListCollaboratorsResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *collaborators.ListCollaboratorsResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.ListCollaboratorsRequest, ...grpc.CallOption) (*collaborators.ListCollaboratorsResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.ListCollaboratorsRequest, ...grpc.CallOption) *collaborators.ListCollaboratorsResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*collaborators.ListCollaboratorsResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.ListCollaboratorsRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListSpaces provides a mock function with given fields: ctx, in, opts
+func (_m *CollaboratorsClient) ListSpaces(ctx context.Context, in *collaborators.ListSpacesRequest, opts ...grpc.CallOption) (*collaborators.ListSpacesResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *collaborators.ListSpacesResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.ListSpacesRequest, ...grpc.CallOption) (*collaborators.ListSpacesResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.ListSpacesRequest, ...grpc.CallOption) *collaborators.ListSpacesResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*collaborators.ListSpacesResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.ListSpacesRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Remove provides a mock function with given fields: ctx, in, opts
+func (_m *CollaboratorsClient) Remove(ctx context.Context, in *collaborators.RemoveRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.RemoveRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.RemoveRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.RemoveRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Set provides a mock function with given fields: ctx, in, opts
+func (_m *CollaboratorsClient) Set(ctx context.Context, in *collaborators.SetRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.SetRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.SetRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.SetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewCollaboratorsClient creates a new instance of CollaboratorsClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollaboratorsClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *CollaboratorsClient {
+	mock := &CollaboratorsClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/CollaboratorsServer.go b/proto/mocks/CollaboratorsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..4e2fe0e00ed5628baa0d2478340f687a13bae9bb
--- /dev/null
+++ b/proto/mocks/CollaboratorsServer.go
@@ -0,0 +1,167 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	collaborators "git.perx.ru/perxis/perxis-go/proto/collaborators"
+
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// CollaboratorsServer is an autogenerated mock type for the CollaboratorsServer type
+type CollaboratorsServer struct {
+	mock.Mock
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *CollaboratorsServer) Get(_a0 context.Context, _a1 *collaborators.GetRequest) (*collaborators.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *collaborators.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.GetRequest) (*collaborators.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.GetRequest) *collaborators.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*collaborators.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.GetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListCollaborators provides a mock function with given fields: _a0, _a1
+func (_m *CollaboratorsServer) ListCollaborators(_a0 context.Context, _a1 *collaborators.ListCollaboratorsRequest) (*collaborators.ListCollaboratorsResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *collaborators.ListCollaboratorsResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.ListCollaboratorsRequest) (*collaborators.ListCollaboratorsResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.ListCollaboratorsRequest) *collaborators.ListCollaboratorsResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*collaborators.ListCollaboratorsResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.ListCollaboratorsRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListSpaces provides a mock function with given fields: _a0, _a1
+func (_m *CollaboratorsServer) ListSpaces(_a0 context.Context, _a1 *collaborators.ListSpacesRequest) (*collaborators.ListSpacesResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *collaborators.ListSpacesResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.ListSpacesRequest) (*collaborators.ListSpacesResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.ListSpacesRequest) *collaborators.ListSpacesResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*collaborators.ListSpacesResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.ListSpacesRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Remove provides a mock function with given fields: _a0, _a1
+func (_m *CollaboratorsServer) Remove(_a0 context.Context, _a1 *collaborators.RemoveRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.RemoveRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.RemoveRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.RemoveRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Set provides a mock function with given fields: _a0, _a1
+func (_m *CollaboratorsServer) Set(_a0 context.Context, _a1 *collaborators.SetRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.SetRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collaborators.SetRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collaborators.SetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedCollaboratorsServer provides a mock function with given fields:
+func (_m *CollaboratorsServer) mustEmbedUnimplementedCollaboratorsServer() {
+	_m.Called()
+}
+
+// NewCollaboratorsServer creates a new instance of CollaboratorsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollaboratorsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *CollaboratorsServer {
+	mock := &CollaboratorsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/CollectionsClient.go b/proto/mocks/CollectionsClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..9c8ae28c4658aaf21f483353d1f98f0f71a52e1e
--- /dev/null
+++ b/proto/mocks/CollectionsClient.go
@@ -0,0 +1,232 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	collections "git.perx.ru/perxis/perxis-go/proto/collections"
+
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	grpc "google.golang.org/grpc"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// CollectionsClient is an autogenerated mock type for the CollectionsClient type
+type CollectionsClient struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: ctx, in, opts
+func (_m *CollectionsClient) Create(ctx context.Context, in *collections.CreateRequest, opts ...grpc.CallOption) (*collections.CreateResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *collections.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.CreateRequest, ...grpc.CallOption) (*collections.CreateResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.CreateRequest, ...grpc.CallOption) *collections.CreateResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*collections.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.CreateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, in, opts
+func (_m *CollectionsClient) Delete(ctx context.Context, in *collections.DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.DeleteRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.DeleteRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.DeleteRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *CollectionsClient) Get(ctx context.Context, in *collections.GetRequest, opts ...grpc.CallOption) (*collections.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *collections.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.GetRequest, ...grpc.CallOption) (*collections.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.GetRequest, ...grpc.CallOption) *collections.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*collections.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.GetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: ctx, in, opts
+func (_m *CollectionsClient) List(ctx context.Context, in *collections.ListRequest, opts ...grpc.CallOption) (*collections.ListResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *collections.ListResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.ListRequest, ...grpc.CallOption) (*collections.ListResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.ListRequest, ...grpc.CallOption) *collections.ListResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*collections.ListResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.ListRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// SetSchema provides a mock function with given fields: ctx, in, opts
+func (_m *CollectionsClient) SetSchema(ctx context.Context, in *collections.SetSchemaRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.SetSchemaRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.SetSchemaRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.SetSchemaRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: ctx, in, opts
+func (_m *CollectionsClient) Update(ctx context.Context, in *collections.UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.UpdateRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.UpdateRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.UpdateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewCollectionsClient creates a new instance of CollectionsClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollectionsClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *CollectionsClient {
+	mock := &CollectionsClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/CollectionsServer.go b/proto/mocks/CollectionsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..5e49e0c96779e8f3262e2b9f6a13ca782f0bb4c7
--- /dev/null
+++ b/proto/mocks/CollectionsServer.go
@@ -0,0 +1,193 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	collections "git.perx.ru/perxis/perxis-go/proto/collections"
+
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// CollectionsServer is an autogenerated mock type for the CollectionsServer type
+type CollectionsServer struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: _a0, _a1
+func (_m *CollectionsServer) Create(_a0 context.Context, _a1 *collections.CreateRequest) (*collections.CreateResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *collections.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.CreateRequest) (*collections.CreateResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.CreateRequest) *collections.CreateResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*collections.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.CreateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: _a0, _a1
+func (_m *CollectionsServer) Delete(_a0 context.Context, _a1 *collections.DeleteRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.DeleteRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.DeleteRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.DeleteRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *CollectionsServer) Get(_a0 context.Context, _a1 *collections.GetRequest) (*collections.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *collections.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.GetRequest) (*collections.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.GetRequest) *collections.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*collections.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.GetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: _a0, _a1
+func (_m *CollectionsServer) List(_a0 context.Context, _a1 *collections.ListRequest) (*collections.ListResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *collections.ListResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.ListRequest) (*collections.ListResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.ListRequest) *collections.ListResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*collections.ListResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.ListRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// SetSchema provides a mock function with given fields: _a0, _a1
+func (_m *CollectionsServer) SetSchema(_a0 context.Context, _a1 *collections.SetSchemaRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.SetSchemaRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.SetSchemaRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.SetSchemaRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: _a0, _a1
+func (_m *CollectionsServer) Update(_a0 context.Context, _a1 *collections.UpdateRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.UpdateRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *collections.UpdateRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *collections.UpdateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedCollectionsServer provides a mock function with given fields:
+func (_m *CollectionsServer) mustEmbedUnimplementedCollectionsServer() {
+	_m.Called()
+}
+
+// NewCollectionsServer creates a new instance of CollectionsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewCollectionsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *CollectionsServer {
+	mock := &CollectionsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/DeliveryClient.go b/proto/mocks/DeliveryClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..21327821d69ce9db1611848da91b243c4e6149ad
--- /dev/null
+++ b/proto/mocks/DeliveryClient.go
@@ -0,0 +1,295 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	delivery "git.perx.ru/perxis/perxis-go/proto/delivery"
+	grpc "google.golang.org/grpc"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// DeliveryClient is an autogenerated mock type for the DeliveryClient type
+type DeliveryClient struct {
+	mock.Mock
+}
+
+// Aggregate provides a mock function with given fields: ctx, in, opts
+func (_m *DeliveryClient) Aggregate(ctx context.Context, in *delivery.AggregateRequest, opts ...grpc.CallOption) (*delivery.AggregateResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *delivery.AggregateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.AggregateRequest, ...grpc.CallOption) (*delivery.AggregateResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.AggregateRequest, ...grpc.CallOption) *delivery.AggregateResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.AggregateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.AggregateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindItems provides a mock function with given fields: ctx, in, opts
+func (_m *DeliveryClient) FindItems(ctx context.Context, in *delivery.FindItemsRequest, opts ...grpc.CallOption) (*delivery.FindItemsResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *delivery.FindItemsResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.FindItemsRequest, ...grpc.CallOption) (*delivery.FindItemsResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.FindItemsRequest, ...grpc.CallOption) *delivery.FindItemsResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.FindItemsResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.FindItemsRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetCollection provides a mock function with given fields: ctx, in, opts
+func (_m *DeliveryClient) GetCollection(ctx context.Context, in *delivery.GetCollectionRequest, opts ...grpc.CallOption) (*delivery.GetCollectionResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *delivery.GetCollectionResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.GetCollectionRequest, ...grpc.CallOption) (*delivery.GetCollectionResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.GetCollectionRequest, ...grpc.CallOption) *delivery.GetCollectionResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.GetCollectionResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.GetCollectionRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetEnvironment provides a mock function with given fields: ctx, in, opts
+func (_m *DeliveryClient) GetEnvironment(ctx context.Context, in *delivery.GetEnvironmentRequest, opts ...grpc.CallOption) (*delivery.GetEnvironmentResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *delivery.GetEnvironmentResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.GetEnvironmentRequest, ...grpc.CallOption) (*delivery.GetEnvironmentResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.GetEnvironmentRequest, ...grpc.CallOption) *delivery.GetEnvironmentResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.GetEnvironmentResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.GetEnvironmentRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetItem provides a mock function with given fields: ctx, in, opts
+func (_m *DeliveryClient) GetItem(ctx context.Context, in *delivery.GetItemRequest, opts ...grpc.CallOption) (*delivery.GetItemResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *delivery.GetItemResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.GetItemRequest, ...grpc.CallOption) (*delivery.GetItemResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.GetItemRequest, ...grpc.CallOption) *delivery.GetItemResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.GetItemResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.GetItemRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListCollections provides a mock function with given fields: ctx, in, opts
+func (_m *DeliveryClient) ListCollections(ctx context.Context, in *delivery.ListCollectionsRequest, opts ...grpc.CallOption) (*delivery.ListCollectionsResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *delivery.ListCollectionsResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.ListCollectionsRequest, ...grpc.CallOption) (*delivery.ListCollectionsResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.ListCollectionsRequest, ...grpc.CallOption) *delivery.ListCollectionsResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.ListCollectionsResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.ListCollectionsRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListEnvironments provides a mock function with given fields: ctx, in, opts
+func (_m *DeliveryClient) ListEnvironments(ctx context.Context, in *delivery.ListEnvironmentsRequest, opts ...grpc.CallOption) (*delivery.ListEnvironmentsResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *delivery.ListEnvironmentsResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.ListEnvironmentsRequest, ...grpc.CallOption) (*delivery.ListEnvironmentsResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.ListEnvironmentsRequest, ...grpc.CallOption) *delivery.ListEnvironmentsResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.ListEnvironmentsResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.ListEnvironmentsRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListLocales provides a mock function with given fields: ctx, in, opts
+func (_m *DeliveryClient) ListLocales(ctx context.Context, in *delivery.ListLocalesRequest, opts ...grpc.CallOption) (*delivery.ListLocalesResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *delivery.ListLocalesResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.ListLocalesRequest, ...grpc.CallOption) (*delivery.ListLocalesResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.ListLocalesRequest, ...grpc.CallOption) *delivery.ListLocalesResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.ListLocalesResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.ListLocalesRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewDeliveryClient creates a new instance of DeliveryClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewDeliveryClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *DeliveryClient {
+	mock := &DeliveryClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/DeliveryServer.go b/proto/mocks/DeliveryServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..2b42c46696a6076c767e904c97ea605a4ac7f738
--- /dev/null
+++ b/proto/mocks/DeliveryServer.go
@@ -0,0 +1,242 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	delivery "git.perx.ru/perxis/perxis-go/proto/delivery"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// DeliveryServer is an autogenerated mock type for the DeliveryServer type
+type DeliveryServer struct {
+	mock.Mock
+}
+
+// Aggregate provides a mock function with given fields: _a0, _a1
+func (_m *DeliveryServer) Aggregate(_a0 context.Context, _a1 *delivery.AggregateRequest) (*delivery.AggregateResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *delivery.AggregateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.AggregateRequest) (*delivery.AggregateResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.AggregateRequest) *delivery.AggregateResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.AggregateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.AggregateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindItems provides a mock function with given fields: _a0, _a1
+func (_m *DeliveryServer) FindItems(_a0 context.Context, _a1 *delivery.FindItemsRequest) (*delivery.FindItemsResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *delivery.FindItemsResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.FindItemsRequest) (*delivery.FindItemsResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.FindItemsRequest) *delivery.FindItemsResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.FindItemsResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.FindItemsRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetCollection provides a mock function with given fields: _a0, _a1
+func (_m *DeliveryServer) GetCollection(_a0 context.Context, _a1 *delivery.GetCollectionRequest) (*delivery.GetCollectionResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *delivery.GetCollectionResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.GetCollectionRequest) (*delivery.GetCollectionResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.GetCollectionRequest) *delivery.GetCollectionResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.GetCollectionResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.GetCollectionRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetEnvironment provides a mock function with given fields: _a0, _a1
+func (_m *DeliveryServer) GetEnvironment(_a0 context.Context, _a1 *delivery.GetEnvironmentRequest) (*delivery.GetEnvironmentResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *delivery.GetEnvironmentResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.GetEnvironmentRequest) (*delivery.GetEnvironmentResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.GetEnvironmentRequest) *delivery.GetEnvironmentResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.GetEnvironmentResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.GetEnvironmentRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetItem provides a mock function with given fields: _a0, _a1
+func (_m *DeliveryServer) GetItem(_a0 context.Context, _a1 *delivery.GetItemRequest) (*delivery.GetItemResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *delivery.GetItemResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.GetItemRequest) (*delivery.GetItemResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.GetItemRequest) *delivery.GetItemResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.GetItemResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.GetItemRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListCollections provides a mock function with given fields: _a0, _a1
+func (_m *DeliveryServer) ListCollections(_a0 context.Context, _a1 *delivery.ListCollectionsRequest) (*delivery.ListCollectionsResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *delivery.ListCollectionsResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.ListCollectionsRequest) (*delivery.ListCollectionsResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.ListCollectionsRequest) *delivery.ListCollectionsResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.ListCollectionsResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.ListCollectionsRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListEnvironments provides a mock function with given fields: _a0, _a1
+func (_m *DeliveryServer) ListEnvironments(_a0 context.Context, _a1 *delivery.ListEnvironmentsRequest) (*delivery.ListEnvironmentsResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *delivery.ListEnvironmentsResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.ListEnvironmentsRequest) (*delivery.ListEnvironmentsResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.ListEnvironmentsRequest) *delivery.ListEnvironmentsResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.ListEnvironmentsResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.ListEnvironmentsRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListLocales provides a mock function with given fields: _a0, _a1
+func (_m *DeliveryServer) ListLocales(_a0 context.Context, _a1 *delivery.ListLocalesRequest) (*delivery.ListLocalesResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *delivery.ListLocalesResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.ListLocalesRequest) (*delivery.ListLocalesResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *delivery.ListLocalesRequest) *delivery.ListLocalesResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*delivery.ListLocalesResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *delivery.ListLocalesRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedDeliveryServer provides a mock function with given fields:
+func (_m *DeliveryServer) mustEmbedUnimplementedDeliveryServer() {
+	_m.Called()
+}
+
+// NewDeliveryServer creates a new instance of DeliveryServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewDeliveryServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *DeliveryServer {
+	mock := &DeliveryServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/EnvironmentsClient.go b/proto/mocks/EnvironmentsClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..9dfa19055c8f93806b238c7b29d9c04d1b90dfb2
--- /dev/null
+++ b/proto/mocks/EnvironmentsClient.go
@@ -0,0 +1,297 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	environments "git.perx.ru/perxis/perxis-go/proto/environments"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	grpc "google.golang.org/grpc"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// EnvironmentsClient is an autogenerated mock type for the EnvironmentsClient type
+type EnvironmentsClient struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: ctx, in, opts
+func (_m *EnvironmentsClient) Create(ctx context.Context, in *environments.CreateRequest, opts ...grpc.CallOption) (*environments.CreateResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *environments.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.CreateRequest, ...grpc.CallOption) (*environments.CreateResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.CreateRequest, ...grpc.CallOption) *environments.CreateResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*environments.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.CreateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, in, opts
+func (_m *EnvironmentsClient) Delete(ctx context.Context, in *environments.DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.DeleteRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.DeleteRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.DeleteRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *EnvironmentsClient) Get(ctx context.Context, in *environments.GetRequest, opts ...grpc.CallOption) (*environments.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *environments.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.GetRequest, ...grpc.CallOption) (*environments.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.GetRequest, ...grpc.CallOption) *environments.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*environments.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.GetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: ctx, in, opts
+func (_m *EnvironmentsClient) List(ctx context.Context, in *environments.ListRequest, opts ...grpc.CallOption) (*environments.ListResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *environments.ListResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.ListRequest, ...grpc.CallOption) (*environments.ListResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.ListRequest, ...grpc.CallOption) *environments.ListResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*environments.ListResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.ListRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Migrate provides a mock function with given fields: ctx, in, opts
+func (_m *EnvironmentsClient) Migrate(ctx context.Context, in *environments.MigrateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.MigrateRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.MigrateRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.MigrateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// RemoveAlias provides a mock function with given fields: ctx, in, opts
+func (_m *EnvironmentsClient) RemoveAlias(ctx context.Context, in *environments.RemoveAliasRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.RemoveAliasRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.RemoveAliasRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.RemoveAliasRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// SetAlias provides a mock function with given fields: ctx, in, opts
+func (_m *EnvironmentsClient) SetAlias(ctx context.Context, in *environments.SetAliasRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.SetAliasRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.SetAliasRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.SetAliasRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: ctx, in, opts
+func (_m *EnvironmentsClient) Update(ctx context.Context, in *environments.UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.UpdateRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.UpdateRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.UpdateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewEnvironmentsClient creates a new instance of EnvironmentsClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewEnvironmentsClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *EnvironmentsClient {
+	mock := &EnvironmentsClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/EnvironmentsServer.go b/proto/mocks/EnvironmentsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..e62b612e0eac04d79bbd24550b4c80b8c421bf6b
--- /dev/null
+++ b/proto/mocks/EnvironmentsServer.go
@@ -0,0 +1,244 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	environments "git.perx.ru/perxis/perxis-go/proto/environments"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// EnvironmentsServer is an autogenerated mock type for the EnvironmentsServer type
+type EnvironmentsServer struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: _a0, _a1
+func (_m *EnvironmentsServer) Create(_a0 context.Context, _a1 *environments.CreateRequest) (*environments.CreateResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *environments.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.CreateRequest) (*environments.CreateResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.CreateRequest) *environments.CreateResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*environments.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.CreateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: _a0, _a1
+func (_m *EnvironmentsServer) Delete(_a0 context.Context, _a1 *environments.DeleteRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.DeleteRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.DeleteRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.DeleteRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *EnvironmentsServer) Get(_a0 context.Context, _a1 *environments.GetRequest) (*environments.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *environments.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.GetRequest) (*environments.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.GetRequest) *environments.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*environments.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.GetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: _a0, _a1
+func (_m *EnvironmentsServer) List(_a0 context.Context, _a1 *environments.ListRequest) (*environments.ListResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *environments.ListResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.ListRequest) (*environments.ListResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.ListRequest) *environments.ListResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*environments.ListResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.ListRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Migrate provides a mock function with given fields: _a0, _a1
+func (_m *EnvironmentsServer) Migrate(_a0 context.Context, _a1 *environments.MigrateRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.MigrateRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.MigrateRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.MigrateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// RemoveAlias provides a mock function with given fields: _a0, _a1
+func (_m *EnvironmentsServer) RemoveAlias(_a0 context.Context, _a1 *environments.RemoveAliasRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.RemoveAliasRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.RemoveAliasRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.RemoveAliasRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// SetAlias provides a mock function with given fields: _a0, _a1
+func (_m *EnvironmentsServer) SetAlias(_a0 context.Context, _a1 *environments.SetAliasRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.SetAliasRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.SetAliasRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.SetAliasRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: _a0, _a1
+func (_m *EnvironmentsServer) Update(_a0 context.Context, _a1 *environments.UpdateRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.UpdateRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *environments.UpdateRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *environments.UpdateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedEnvironmentsServer provides a mock function with given fields:
+func (_m *EnvironmentsServer) mustEmbedUnimplementedEnvironmentsServer() {
+	_m.Called()
+}
+
+// NewEnvironmentsServer creates a new instance of EnvironmentsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewEnvironmentsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *EnvironmentsServer {
+	mock := &EnvironmentsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/FilesClient.go b/proto/mocks/FilesClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..b935c55866ac5928a9df36cb1d5e3c9e97598269
--- /dev/null
+++ b/proto/mocks/FilesClient.go
@@ -0,0 +1,264 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	files "git.perx.ru/perxis/perxis-go/proto/files"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	grpc "google.golang.org/grpc"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// FilesClient is an autogenerated mock type for the FilesClient type
+type FilesClient struct {
+	mock.Mock
+}
+
+// AbortUpload provides a mock function with given fields: ctx, in, opts
+func (_m *FilesClient) AbortUpload(ctx context.Context, in *files.AbortUploadRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.AbortUploadRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.AbortUploadRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.AbortUploadRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CompleteUpload provides a mock function with given fields: ctx, in, opts
+func (_m *FilesClient) CompleteUpload(ctx context.Context, in *files.CompleteUploadRequest, opts ...grpc.CallOption) (*files.CompleteUploadResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *files.CompleteUploadResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.CompleteUploadRequest, ...grpc.CallOption) (*files.CompleteUploadResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.CompleteUploadRequest, ...grpc.CallOption) *files.CompleteUploadResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*files.CompleteUploadResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.CompleteUploadRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DeleteFile provides a mock function with given fields: ctx, in, opts
+func (_m *FilesClient) DeleteFile(ctx context.Context, in *files.DeleteFileRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.DeleteFileRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.DeleteFileRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.DeleteFileRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetFile provides a mock function with given fields: ctx, in, opts
+func (_m *FilesClient) GetFile(ctx context.Context, in *files.GetFileRequest, opts ...grpc.CallOption) (*files.GetFileResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *files.GetFileResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.GetFileRequest, ...grpc.CallOption) (*files.GetFileResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.GetFileRequest, ...grpc.CallOption) *files.GetFileResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*files.GetFileResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.GetFileRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// MoveUpload provides a mock function with given fields: ctx, in, opts
+func (_m *FilesClient) MoveUpload(ctx context.Context, in *files.MoveUploadRequest, opts ...grpc.CallOption) (*files.MoveUploadResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *files.MoveUploadResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.MoveUploadRequest, ...grpc.CallOption) (*files.MoveUploadResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.MoveUploadRequest, ...grpc.CallOption) *files.MoveUploadResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*files.MoveUploadResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.MoveUploadRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// StartUpload provides a mock function with given fields: ctx, in, opts
+func (_m *FilesClient) StartUpload(ctx context.Context, in *files.StartUploadRequest, opts ...grpc.CallOption) (*files.StartUploadResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *files.StartUploadResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.StartUploadRequest, ...grpc.CallOption) (*files.StartUploadResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.StartUploadRequest, ...grpc.CallOption) *files.StartUploadResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*files.StartUploadResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.StartUploadRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Upload provides a mock function with given fields: ctx, in, opts
+func (_m *FilesClient) Upload(ctx context.Context, in *files.UploadRequest, opts ...grpc.CallOption) (*files.UploadResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *files.UploadResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.UploadRequest, ...grpc.CallOption) (*files.UploadResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.UploadRequest, ...grpc.CallOption) *files.UploadResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*files.UploadResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.UploadRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewFilesClient creates a new instance of FilesClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewFilesClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *FilesClient {
+	mock := &FilesClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/FilesServer.go b/proto/mocks/FilesServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..3040249d215cfc9edd3376ef08c8697c6d8c3ced
--- /dev/null
+++ b/proto/mocks/FilesServer.go
@@ -0,0 +1,218 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	files "git.perx.ru/perxis/perxis-go/proto/files"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// FilesServer is an autogenerated mock type for the FilesServer type
+type FilesServer struct {
+	mock.Mock
+}
+
+// AbortUpload provides a mock function with given fields: _a0, _a1
+func (_m *FilesServer) AbortUpload(_a0 context.Context, _a1 *files.AbortUploadRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.AbortUploadRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.AbortUploadRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.AbortUploadRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// CompleteUpload provides a mock function with given fields: _a0, _a1
+func (_m *FilesServer) CompleteUpload(_a0 context.Context, _a1 *files.CompleteUploadRequest) (*files.CompleteUploadResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *files.CompleteUploadResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.CompleteUploadRequest) (*files.CompleteUploadResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.CompleteUploadRequest) *files.CompleteUploadResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*files.CompleteUploadResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.CompleteUploadRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// DeleteFile provides a mock function with given fields: _a0, _a1
+func (_m *FilesServer) DeleteFile(_a0 context.Context, _a1 *files.DeleteFileRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.DeleteFileRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.DeleteFileRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.DeleteFileRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetFile provides a mock function with given fields: _a0, _a1
+func (_m *FilesServer) GetFile(_a0 context.Context, _a1 *files.GetFileRequest) (*files.GetFileResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *files.GetFileResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.GetFileRequest) (*files.GetFileResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.GetFileRequest) *files.GetFileResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*files.GetFileResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.GetFileRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// MoveUpload provides a mock function with given fields: _a0, _a1
+func (_m *FilesServer) MoveUpload(_a0 context.Context, _a1 *files.MoveUploadRequest) (*files.MoveUploadResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *files.MoveUploadResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.MoveUploadRequest) (*files.MoveUploadResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.MoveUploadRequest) *files.MoveUploadResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*files.MoveUploadResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.MoveUploadRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// StartUpload provides a mock function with given fields: _a0, _a1
+func (_m *FilesServer) StartUpload(_a0 context.Context, _a1 *files.StartUploadRequest) (*files.StartUploadResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *files.StartUploadResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.StartUploadRequest) (*files.StartUploadResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.StartUploadRequest) *files.StartUploadResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*files.StartUploadResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.StartUploadRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Upload provides a mock function with given fields: _a0, _a1
+func (_m *FilesServer) Upload(_a0 context.Context, _a1 *files.UploadRequest) (*files.UploadResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *files.UploadResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *files.UploadRequest) (*files.UploadResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *files.UploadRequest) *files.UploadResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*files.UploadResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *files.UploadRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedFilesServer provides a mock function with given fields:
+func (_m *FilesServer) mustEmbedUnimplementedFilesServer() {
+	_m.Called()
+}
+
+// NewFilesServer creates a new instance of FilesServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewFilesServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *FilesServer {
+	mock := &FilesServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/ImagesClient.go b/proto/mocks/ImagesClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..f0d517b5341725649ddea5002187548bbb6c1ec1
--- /dev/null
+++ b/proto/mocks/ImagesClient.go
@@ -0,0 +1,65 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	grpc "google.golang.org/grpc"
+
+	images "git.perx.ru/perxis/perxis-go/proto/images"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// ImagesClient is an autogenerated mock type for the ImagesClient type
+type ImagesClient struct {
+	mock.Mock
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *ImagesClient) Get(ctx context.Context, in *images.GetRequest, opts ...grpc.CallOption) (*images.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *images.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *images.GetRequest, ...grpc.CallOption) (*images.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *images.GetRequest, ...grpc.CallOption) *images.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*images.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *images.GetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewImagesClient creates a new instance of ImagesClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewImagesClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *ImagesClient {
+	mock := &ImagesClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/ImagesServer.go b/proto/mocks/ImagesServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..48dc9e4719c4cc85c1707a301f53ed4b44656a86
--- /dev/null
+++ b/proto/mocks/ImagesServer.go
@@ -0,0 +1,60 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	images "git.perx.ru/perxis/perxis-go/proto/images"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// ImagesServer is an autogenerated mock type for the ImagesServer type
+type ImagesServer struct {
+	mock.Mock
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *ImagesServer) Get(_a0 context.Context, _a1 *images.GetRequest) (*images.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *images.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *images.GetRequest) (*images.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *images.GetRequest) *images.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*images.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *images.GetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedImagesServer provides a mock function with given fields:
+func (_m *ImagesServer) mustEmbedUnimplementedImagesServer() {
+	_m.Called()
+}
+
+// NewImagesServer creates a new instance of ImagesServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewImagesServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *ImagesServer {
+	mock := &ImagesServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/InvitationsClient.go b/proto/mocks/InvitationsClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..442560a76aabadae72784e35a867c3384acb1d83
--- /dev/null
+++ b/proto/mocks/InvitationsClient.go
@@ -0,0 +1,198 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	grpc "google.golang.org/grpc"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	invitations "git.perx.ru/perxis/perxis-go/proto/invitations"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// InvitationsClient is an autogenerated mock type for the InvitationsClient type
+type InvitationsClient struct {
+	mock.Mock
+}
+
+// Accept provides a mock function with given fields: ctx, in, opts
+func (_m *InvitationsClient) Accept(ctx context.Context, in *invitations.AcceptRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.AcceptRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.AcceptRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *invitations.AcceptRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Create provides a mock function with given fields: ctx, in, opts
+func (_m *InvitationsClient) Create(ctx context.Context, in *invitations.CreateRequest, opts ...grpc.CallOption) (*invitations.CreateResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *invitations.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.CreateRequest, ...grpc.CallOption) (*invitations.CreateResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.CreateRequest, ...grpc.CallOption) *invitations.CreateResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*invitations.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *invitations.CreateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, in, opts
+func (_m *InvitationsClient) Delete(ctx context.Context, in *invitations.DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.DeleteRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.DeleteRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *invitations.DeleteRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Find provides a mock function with given fields: ctx, in, opts
+func (_m *InvitationsClient) Find(ctx context.Context, in *invitations.FindRequest, opts ...grpc.CallOption) (*invitations.FindResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *invitations.FindResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.FindRequest, ...grpc.CallOption) (*invitations.FindResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.FindRequest, ...grpc.CallOption) *invitations.FindResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*invitations.FindResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *invitations.FindRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *InvitationsClient) Get(ctx context.Context, in *invitations.GetRequest, opts ...grpc.CallOption) (*invitations.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *invitations.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.GetRequest, ...grpc.CallOption) (*invitations.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.GetRequest, ...grpc.CallOption) *invitations.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*invitations.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *invitations.GetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewInvitationsClient creates a new instance of InvitationsClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewInvitationsClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *InvitationsClient {
+	mock := &InvitationsClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/InvitationsServer.go b/proto/mocks/InvitationsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..d48c99cdad7727b915c90dca136018d0c6d9d7f8
--- /dev/null
+++ b/proto/mocks/InvitationsServer.go
@@ -0,0 +1,166 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	invitations "git.perx.ru/perxis/perxis-go/proto/invitations"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// InvitationsServer is an autogenerated mock type for the InvitationsServer type
+type InvitationsServer struct {
+	mock.Mock
+}
+
+// Accept provides a mock function with given fields: _a0, _a1
+func (_m *InvitationsServer) Accept(_a0 context.Context, _a1 *invitations.AcceptRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.AcceptRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.AcceptRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *invitations.AcceptRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Create provides a mock function with given fields: _a0, _a1
+func (_m *InvitationsServer) Create(_a0 context.Context, _a1 *invitations.CreateRequest) (*invitations.CreateResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *invitations.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.CreateRequest) (*invitations.CreateResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.CreateRequest) *invitations.CreateResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*invitations.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *invitations.CreateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: _a0, _a1
+func (_m *InvitationsServer) Delete(_a0 context.Context, _a1 *invitations.DeleteRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.DeleteRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.DeleteRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *invitations.DeleteRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Find provides a mock function with given fields: _a0, _a1
+func (_m *InvitationsServer) Find(_a0 context.Context, _a1 *invitations.FindRequest) (*invitations.FindResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *invitations.FindResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.FindRequest) (*invitations.FindResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.FindRequest) *invitations.FindResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*invitations.FindResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *invitations.FindRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *InvitationsServer) Get(_a0 context.Context, _a1 *invitations.GetRequest) (*invitations.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *invitations.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.GetRequest) (*invitations.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *invitations.GetRequest) *invitations.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*invitations.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *invitations.GetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedInvitationsServer provides a mock function with given fields:
+func (_m *InvitationsServer) mustEmbedUnimplementedInvitationsServer() {
+	_m.Called()
+}
+
+// NewInvitationsServer creates a new instance of InvitationsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewInvitationsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *InvitationsServer {
+	mock := &InvitationsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/ItemsClient.go b/proto/mocks/ItemsClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..bf9494d98adfc1413a2691b72e57ef3ec7b89b82
--- /dev/null
+++ b/proto/mocks/ItemsClient.go
@@ -0,0 +1,627 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	grpc "google.golang.org/grpc"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	items "git.perx.ru/perxis/perxis-go/proto/items"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// ItemsClient is an autogenerated mock type for the ItemsClient type
+type ItemsClient struct {
+	mock.Mock
+}
+
+// Aggregate provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) Aggregate(ctx context.Context, in *items.AggregateRequest, opts ...grpc.CallOption) (*items.AggregateResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *items.AggregateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.AggregateRequest, ...grpc.CallOption) (*items.AggregateResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.AggregateRequest, ...grpc.CallOption) *items.AggregateResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.AggregateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.AggregateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// AggregatePublished provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) AggregatePublished(ctx context.Context, in *items.AggregatePublishedRequest, opts ...grpc.CallOption) (*items.AggregatePublishedResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *items.AggregatePublishedResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.AggregatePublishedRequest, ...grpc.CallOption) (*items.AggregatePublishedResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.AggregatePublishedRequest, ...grpc.CallOption) *items.AggregatePublishedResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.AggregatePublishedResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.AggregatePublishedRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Archive provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) Archive(ctx context.Context, in *items.ArchiveRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.ArchiveRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.ArchiveRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.ArchiveRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Create provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) Create(ctx context.Context, in *items.CreateRequest, opts ...grpc.CallOption) (*items.CreateResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *items.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.CreateRequest, ...grpc.CallOption) (*items.CreateResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.CreateRequest, ...grpc.CallOption) *items.CreateResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.CreateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) Delete(ctx context.Context, in *items.DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.DeleteRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.DeleteRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.DeleteRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Find provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) Find(ctx context.Context, in *items.FindRequest, opts ...grpc.CallOption) (*items.FindResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *items.FindResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.FindRequest, ...grpc.CallOption) (*items.FindResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.FindRequest, ...grpc.CallOption) *items.FindResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.FindResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.FindRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindArchived provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) FindArchived(ctx context.Context, in *items.FindArchivedRequest, opts ...grpc.CallOption) (*items.FindArchivedResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *items.FindArchivedResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.FindArchivedRequest, ...grpc.CallOption) (*items.FindArchivedResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.FindArchivedRequest, ...grpc.CallOption) *items.FindArchivedResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.FindArchivedResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.FindArchivedRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindPublished provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) FindPublished(ctx context.Context, in *items.FindPublishedRequest, opts ...grpc.CallOption) (*items.FindPublishedResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *items.FindPublishedResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.FindPublishedRequest, ...grpc.CallOption) (*items.FindPublishedResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.FindPublishedRequest, ...grpc.CallOption) *items.FindPublishedResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.FindPublishedResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.FindPublishedRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) Get(ctx context.Context, in *items.GetRequest, opts ...grpc.CallOption) (*items.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *items.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.GetRequest, ...grpc.CallOption) (*items.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.GetRequest, ...grpc.CallOption) *items.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.GetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetPublished provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) GetPublished(ctx context.Context, in *items.GetPublishedRequest, opts ...grpc.CallOption) (*items.GetPublishedResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *items.GetPublishedResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.GetPublishedRequest, ...grpc.CallOption) (*items.GetPublishedResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.GetPublishedRequest, ...grpc.CallOption) *items.GetPublishedResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.GetPublishedResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.GetPublishedRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetRevision provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) GetRevision(ctx context.Context, in *items.GetRevisionRequest, opts ...grpc.CallOption) (*items.GetRevisionResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *items.GetRevisionResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.GetRevisionRequest, ...grpc.CallOption) (*items.GetRevisionResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.GetRevisionRequest, ...grpc.CallOption) *items.GetRevisionResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.GetRevisionResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.GetRevisionRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Introspect provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) Introspect(ctx context.Context, in *items.IntrospectRequest, opts ...grpc.CallOption) (*items.IntrospectResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *items.IntrospectResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.IntrospectRequest, ...grpc.CallOption) (*items.IntrospectResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.IntrospectRequest, ...grpc.CallOption) *items.IntrospectResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.IntrospectResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.IntrospectRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListRevisions provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) ListRevisions(ctx context.Context, in *items.ListRevisionsRequest, opts ...grpc.CallOption) (*items.ListRevisionsResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *items.ListRevisionsResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.ListRevisionsRequest, ...grpc.CallOption) (*items.ListRevisionsResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.ListRevisionsRequest, ...grpc.CallOption) *items.ListRevisionsResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.ListRevisionsResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.ListRevisionsRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Publish provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) Publish(ctx context.Context, in *items.PublishRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.PublishRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.PublishRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.PublishRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Unarchive provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) Unarchive(ctx context.Context, in *items.UnarchiveRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UnarchiveRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UnarchiveRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.UnarchiveRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Undelete provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) Undelete(ctx context.Context, in *items.UndeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UndeleteRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UndeleteRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.UndeleteRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Unpublish provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) Unpublish(ctx context.Context, in *items.UnpublishRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UnpublishRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UnpublishRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.UnpublishRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: ctx, in, opts
+func (_m *ItemsClient) Update(ctx context.Context, in *items.UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UpdateRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UpdateRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.UpdateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewItemsClient creates a new instance of ItemsClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewItemsClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *ItemsClient {
+	mock := &ItemsClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/ItemsServer.go b/proto/mocks/ItemsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..89cb4de56d09ef13df9cd1da1ed0654d28f4059c
--- /dev/null
+++ b/proto/mocks/ItemsServer.go
@@ -0,0 +1,504 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	items "git.perx.ru/perxis/perxis-go/proto/items"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// ItemsServer is an autogenerated mock type for the ItemsServer type
+type ItemsServer struct {
+	mock.Mock
+}
+
+// Aggregate provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) Aggregate(_a0 context.Context, _a1 *items.AggregateRequest) (*items.AggregateResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *items.AggregateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.AggregateRequest) (*items.AggregateResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.AggregateRequest) *items.AggregateResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.AggregateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.AggregateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// AggregatePublished provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) AggregatePublished(_a0 context.Context, _a1 *items.AggregatePublishedRequest) (*items.AggregatePublishedResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *items.AggregatePublishedResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.AggregatePublishedRequest) (*items.AggregatePublishedResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.AggregatePublishedRequest) *items.AggregatePublishedResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.AggregatePublishedResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.AggregatePublishedRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Archive provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) Archive(_a0 context.Context, _a1 *items.ArchiveRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.ArchiveRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.ArchiveRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.ArchiveRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Create provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) Create(_a0 context.Context, _a1 *items.CreateRequest) (*items.CreateResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *items.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.CreateRequest) (*items.CreateResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.CreateRequest) *items.CreateResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.CreateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) Delete(_a0 context.Context, _a1 *items.DeleteRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.DeleteRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.DeleteRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.DeleteRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Find provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) Find(_a0 context.Context, _a1 *items.FindRequest) (*items.FindResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *items.FindResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.FindRequest) (*items.FindResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.FindRequest) *items.FindResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.FindResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.FindRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindArchived provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) FindArchived(_a0 context.Context, _a1 *items.FindArchivedRequest) (*items.FindArchivedResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *items.FindArchivedResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.FindArchivedRequest) (*items.FindArchivedResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.FindArchivedRequest) *items.FindArchivedResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.FindArchivedResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.FindArchivedRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// FindPublished provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) FindPublished(_a0 context.Context, _a1 *items.FindPublishedRequest) (*items.FindPublishedResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *items.FindPublishedResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.FindPublishedRequest) (*items.FindPublishedResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.FindPublishedRequest) *items.FindPublishedResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.FindPublishedResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.FindPublishedRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) Get(_a0 context.Context, _a1 *items.GetRequest) (*items.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *items.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.GetRequest) (*items.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.GetRequest) *items.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.GetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetPublished provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) GetPublished(_a0 context.Context, _a1 *items.GetPublishedRequest) (*items.GetPublishedResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *items.GetPublishedResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.GetPublishedRequest) (*items.GetPublishedResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.GetPublishedRequest) *items.GetPublishedResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.GetPublishedResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.GetPublishedRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetRevision provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) GetRevision(_a0 context.Context, _a1 *items.GetRevisionRequest) (*items.GetRevisionResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *items.GetRevisionResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.GetRevisionRequest) (*items.GetRevisionResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.GetRevisionRequest) *items.GetRevisionResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.GetRevisionResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.GetRevisionRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Introspect provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) Introspect(_a0 context.Context, _a1 *items.IntrospectRequest) (*items.IntrospectResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *items.IntrospectResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.IntrospectRequest) (*items.IntrospectResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.IntrospectRequest) *items.IntrospectResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.IntrospectResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.IntrospectRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListRevisions provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) ListRevisions(_a0 context.Context, _a1 *items.ListRevisionsRequest) (*items.ListRevisionsResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *items.ListRevisionsResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.ListRevisionsRequest) (*items.ListRevisionsResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.ListRevisionsRequest) *items.ListRevisionsResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*items.ListRevisionsResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.ListRevisionsRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Publish provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) Publish(_a0 context.Context, _a1 *items.PublishRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.PublishRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.PublishRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.PublishRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Unarchive provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) Unarchive(_a0 context.Context, _a1 *items.UnarchiveRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UnarchiveRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UnarchiveRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.UnarchiveRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Undelete provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) Undelete(_a0 context.Context, _a1 *items.UndeleteRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UndeleteRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UndeleteRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.UndeleteRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Unpublish provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) Unpublish(_a0 context.Context, _a1 *items.UnpublishRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UnpublishRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UnpublishRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.UnpublishRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: _a0, _a1
+func (_m *ItemsServer) Update(_a0 context.Context, _a1 *items.UpdateRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UpdateRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *items.UpdateRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *items.UpdateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedItemsServer provides a mock function with given fields:
+func (_m *ItemsServer) mustEmbedUnimplementedItemsServer() {
+	_m.Called()
+}
+
+// NewItemsServer creates a new instance of ItemsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewItemsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *ItemsServer {
+	mock := &ItemsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/LocalesClient.go b/proto/mocks/LocalesClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..8ed98fd037a2b6ee59ba0ecb379ae7cca62f81c3
--- /dev/null
+++ b/proto/mocks/LocalesClient.go
@@ -0,0 +1,132 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	grpc "google.golang.org/grpc"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	locales "git.perx.ru/perxis/perxis-go/proto/locales"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// LocalesClient is an autogenerated mock type for the LocalesClient type
+type LocalesClient struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: ctx, in, opts
+func (_m *LocalesClient) Create(ctx context.Context, in *locales.CreateRequest, opts ...grpc.CallOption) (*locales.CreateResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *locales.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.CreateRequest, ...grpc.CallOption) (*locales.CreateResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.CreateRequest, ...grpc.CallOption) *locales.CreateResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*locales.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *locales.CreateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, in, opts
+func (_m *LocalesClient) Delete(ctx context.Context, in *locales.DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.DeleteRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.DeleteRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *locales.DeleteRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: ctx, in, opts
+func (_m *LocalesClient) List(ctx context.Context, in *locales.ListRequest, opts ...grpc.CallOption) (*locales.ListResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *locales.ListResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.ListRequest, ...grpc.CallOption) (*locales.ListResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.ListRequest, ...grpc.CallOption) *locales.ListResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*locales.ListResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *locales.ListRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewLocalesClient creates a new instance of LocalesClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewLocalesClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *LocalesClient {
+	mock := &LocalesClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/LocalesServer.go b/proto/mocks/LocalesServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..8afc56d23f226ac41f44a58d1c7435c474ccde90
--- /dev/null
+++ b/proto/mocks/LocalesServer.go
@@ -0,0 +1,114 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	locales "git.perx.ru/perxis/perxis-go/proto/locales"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// LocalesServer is an autogenerated mock type for the LocalesServer type
+type LocalesServer struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: _a0, _a1
+func (_m *LocalesServer) Create(_a0 context.Context, _a1 *locales.CreateRequest) (*locales.CreateResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *locales.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.CreateRequest) (*locales.CreateResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.CreateRequest) *locales.CreateResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*locales.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *locales.CreateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: _a0, _a1
+func (_m *LocalesServer) Delete(_a0 context.Context, _a1 *locales.DeleteRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.DeleteRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.DeleteRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *locales.DeleteRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: _a0, _a1
+func (_m *LocalesServer) List(_a0 context.Context, _a1 *locales.ListRequest) (*locales.ListResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *locales.ListResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.ListRequest) (*locales.ListResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *locales.ListRequest) *locales.ListResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*locales.ListResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *locales.ListRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedLocalesServer provides a mock function with given fields:
+func (_m *LocalesServer) mustEmbedUnimplementedLocalesServer() {
+	_m.Called()
+}
+
+// NewLocalesServer creates a new instance of LocalesServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewLocalesServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *LocalesServer {
+	mock := &LocalesServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/MembersClient.go b/proto/mocks/MembersClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..0e24c1c47873ae473bacb6ce434b2eb6e0ff0765
--- /dev/null
+++ b/proto/mocks/MembersClient.go
@@ -0,0 +1,198 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	grpc "google.golang.org/grpc"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	members "git.perx.ru/perxis/perxis-go/proto/members"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// MembersClient is an autogenerated mock type for the MembersClient type
+type MembersClient struct {
+	mock.Mock
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *MembersClient) Get(ctx context.Context, in *members.GetRequest, opts ...grpc.CallOption) (*members.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *members.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.GetRequest, ...grpc.CallOption) (*members.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *members.GetRequest, ...grpc.CallOption) *members.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*members.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *members.GetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListMembers provides a mock function with given fields: ctx, in, opts
+func (_m *MembersClient) ListMembers(ctx context.Context, in *members.ListMembersRequest, opts ...grpc.CallOption) (*members.ListMembersResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *members.ListMembersResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.ListMembersRequest, ...grpc.CallOption) (*members.ListMembersResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *members.ListMembersRequest, ...grpc.CallOption) *members.ListMembersResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*members.ListMembersResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *members.ListMembersRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListOrganizations provides a mock function with given fields: ctx, in, opts
+func (_m *MembersClient) ListOrganizations(ctx context.Context, in *members.ListOrganizationsRequest, opts ...grpc.CallOption) (*members.ListOrganizationsResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *members.ListOrganizationsResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.ListOrganizationsRequest, ...grpc.CallOption) (*members.ListOrganizationsResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *members.ListOrganizationsRequest, ...grpc.CallOption) *members.ListOrganizationsResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*members.ListOrganizationsResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *members.ListOrganizationsRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Remove provides a mock function with given fields: ctx, in, opts
+func (_m *MembersClient) Remove(ctx context.Context, in *members.RemoveRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.RemoveRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *members.RemoveRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *members.RemoveRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Set provides a mock function with given fields: ctx, in, opts
+func (_m *MembersClient) Set(ctx context.Context, in *members.SetRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.SetRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *members.SetRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *members.SetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewMembersClient creates a new instance of MembersClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMembersClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *MembersClient {
+	mock := &MembersClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/MembersServer.go b/proto/mocks/MembersServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..6adaf990dcf17a092711fac6b4ca31c4561ee103
--- /dev/null
+++ b/proto/mocks/MembersServer.go
@@ -0,0 +1,166 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	members "git.perx.ru/perxis/perxis-go/proto/members"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// MembersServer is an autogenerated mock type for the MembersServer type
+type MembersServer struct {
+	mock.Mock
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *MembersServer) Get(_a0 context.Context, _a1 *members.GetRequest) (*members.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *members.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.GetRequest) (*members.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *members.GetRequest) *members.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*members.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *members.GetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListMembers provides a mock function with given fields: _a0, _a1
+func (_m *MembersServer) ListMembers(_a0 context.Context, _a1 *members.ListMembersRequest) (*members.ListMembersResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *members.ListMembersResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.ListMembersRequest) (*members.ListMembersResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *members.ListMembersRequest) *members.ListMembersResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*members.ListMembersResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *members.ListMembersRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListOrganizations provides a mock function with given fields: _a0, _a1
+func (_m *MembersServer) ListOrganizations(_a0 context.Context, _a1 *members.ListOrganizationsRequest) (*members.ListOrganizationsResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *members.ListOrganizationsResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.ListOrganizationsRequest) (*members.ListOrganizationsResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *members.ListOrganizationsRequest) *members.ListOrganizationsResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*members.ListOrganizationsResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *members.ListOrganizationsRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Remove provides a mock function with given fields: _a0, _a1
+func (_m *MembersServer) Remove(_a0 context.Context, _a1 *members.RemoveRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.RemoveRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *members.RemoveRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *members.RemoveRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Set provides a mock function with given fields: _a0, _a1
+func (_m *MembersServer) Set(_a0 context.Context, _a1 *members.SetRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.SetRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *members.SetRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *members.SetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedMembersServer provides a mock function with given fields:
+func (_m *MembersServer) mustEmbedUnimplementedMembersServer() {
+	_m.Called()
+}
+
+// NewMembersServer creates a new instance of MembersServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewMembersServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *MembersServer {
+	mock := &MembersServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/ObserverClient.go b/proto/mocks/ObserverClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..7e5463516e7a70aa9826185791a1bd8ebfbcb241
--- /dev/null
+++ b/proto/mocks/ObserverClient.go
@@ -0,0 +1,65 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	grpc "google.golang.org/grpc"
+
+	members "git.perx.ru/perxis/perxis-go/proto/members"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// ObserverClient is an autogenerated mock type for the ObserverClient type
+type ObserverClient struct {
+	mock.Mock
+}
+
+// OnCollaboratorSet provides a mock function with given fields: ctx, in, opts
+func (_m *ObserverClient) OnCollaboratorSet(ctx context.Context, in *members.OnCollaboratorSetRequest, opts ...grpc.CallOption) (*members.OnCollaboratorSetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *members.OnCollaboratorSetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.OnCollaboratorSetRequest, ...grpc.CallOption) (*members.OnCollaboratorSetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *members.OnCollaboratorSetRequest, ...grpc.CallOption) *members.OnCollaboratorSetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*members.OnCollaboratorSetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *members.OnCollaboratorSetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewObserverClient creates a new instance of ObserverClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewObserverClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *ObserverClient {
+	mock := &ObserverClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/ObserverServer.go b/proto/mocks/ObserverServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..87cba94c518b9cb06d725a5dd0224a29f4a7bbc9
--- /dev/null
+++ b/proto/mocks/ObserverServer.go
@@ -0,0 +1,60 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	members "git.perx.ru/perxis/perxis-go/proto/members"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// ObserverServer is an autogenerated mock type for the ObserverServer type
+type ObserverServer struct {
+	mock.Mock
+}
+
+// OnCollaboratorSet provides a mock function with given fields: _a0, _a1
+func (_m *ObserverServer) OnCollaboratorSet(_a0 context.Context, _a1 *members.OnCollaboratorSetRequest) (*members.OnCollaboratorSetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *members.OnCollaboratorSetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *members.OnCollaboratorSetRequest) (*members.OnCollaboratorSetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *members.OnCollaboratorSetRequest) *members.OnCollaboratorSetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*members.OnCollaboratorSetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *members.OnCollaboratorSetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedObserverServer provides a mock function with given fields:
+func (_m *ObserverServer) mustEmbedUnimplementedObserverServer() {
+	_m.Called()
+}
+
+// NewObserverServer creates a new instance of ObserverServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewObserverServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *ObserverServer {
+	mock := &ObserverServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/OperationServiceClient.go b/proto/mocks/OperationServiceClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..4118237072bf6a086cd211a0e04de528b6b259be
--- /dev/null
+++ b/proto/mocks/OperationServiceClient.go
@@ -0,0 +1,98 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	common "git.perx.ru/perxis/perxis-go/proto/common"
+
+	grpc "google.golang.org/grpc"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// OperationServiceClient is an autogenerated mock type for the OperationServiceClient type
+type OperationServiceClient struct {
+	mock.Mock
+}
+
+// Cancel provides a mock function with given fields: ctx, in, opts
+func (_m *OperationServiceClient) Cancel(ctx context.Context, in *common.CancelOperationRequest, opts ...grpc.CallOption) (*common.Operation, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *common.Operation
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *common.CancelOperationRequest, ...grpc.CallOption) (*common.Operation, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *common.CancelOperationRequest, ...grpc.CallOption) *common.Operation); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*common.Operation)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *common.CancelOperationRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *OperationServiceClient) Get(ctx context.Context, in *common.GetOperationRequest, opts ...grpc.CallOption) (*common.Operation, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *common.Operation
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *common.GetOperationRequest, ...grpc.CallOption) (*common.Operation, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *common.GetOperationRequest, ...grpc.CallOption) *common.Operation); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*common.Operation)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *common.GetOperationRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewOperationServiceClient creates a new instance of OperationServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewOperationServiceClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *OperationServiceClient {
+	mock := &OperationServiceClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/OperationServiceServer.go b/proto/mocks/OperationServiceServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..661a205ea0050fa4c5c33dd2eb13a824f9cfc24d
--- /dev/null
+++ b/proto/mocks/OperationServiceServer.go
@@ -0,0 +1,87 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	common "git.perx.ru/perxis/perxis-go/proto/common"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// OperationServiceServer is an autogenerated mock type for the OperationServiceServer type
+type OperationServiceServer struct {
+	mock.Mock
+}
+
+// Cancel provides a mock function with given fields: _a0, _a1
+func (_m *OperationServiceServer) Cancel(_a0 context.Context, _a1 *common.CancelOperationRequest) (*common.Operation, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *common.Operation
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *common.CancelOperationRequest) (*common.Operation, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *common.CancelOperationRequest) *common.Operation); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*common.Operation)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *common.CancelOperationRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *OperationServiceServer) Get(_a0 context.Context, _a1 *common.GetOperationRequest) (*common.Operation, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *common.Operation
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *common.GetOperationRequest) (*common.Operation, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *common.GetOperationRequest) *common.Operation); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*common.Operation)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *common.GetOperationRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedOperationServiceServer provides a mock function with given fields:
+func (_m *OperationServiceServer) mustEmbedUnimplementedOperationServiceServer() {
+	_m.Called()
+}
+
+// NewOperationServiceServer creates a new instance of OperationServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewOperationServiceServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *OperationServiceServer {
+	mock := &OperationServiceServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/OrganizationsClient.go b/proto/mocks/OrganizationsClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..972d316dd0e1507b98ddc096709cc93acd5b80d2
--- /dev/null
+++ b/proto/mocks/OrganizationsClient.go
@@ -0,0 +1,198 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	grpc "google.golang.org/grpc"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+
+	organizations "git.perx.ru/perxis/perxis-go/proto/organizations"
+)
+
+// OrganizationsClient is an autogenerated mock type for the OrganizationsClient type
+type OrganizationsClient struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: ctx, in, opts
+func (_m *OrganizationsClient) Create(ctx context.Context, in *organizations.CreateRequest, opts ...grpc.CallOption) (*organizations.CreateResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *organizations.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.CreateRequest, ...grpc.CallOption) (*organizations.CreateResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.CreateRequest, ...grpc.CallOption) *organizations.CreateResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*organizations.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *organizations.CreateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, in, opts
+func (_m *OrganizationsClient) Delete(ctx context.Context, in *organizations.DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.DeleteRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.DeleteRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *organizations.DeleteRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Find provides a mock function with given fields: ctx, in, opts
+func (_m *OrganizationsClient) Find(ctx context.Context, in *organizations.FindRequest, opts ...grpc.CallOption) (*organizations.FindResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *organizations.FindResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.FindRequest, ...grpc.CallOption) (*organizations.FindResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.FindRequest, ...grpc.CallOption) *organizations.FindResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*organizations.FindResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *organizations.FindRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *OrganizationsClient) Get(ctx context.Context, in *organizations.GetRequest, opts ...grpc.CallOption) (*organizations.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *organizations.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.GetRequest, ...grpc.CallOption) (*organizations.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.GetRequest, ...grpc.CallOption) *organizations.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*organizations.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *organizations.GetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: ctx, in, opts
+func (_m *OrganizationsClient) Update(ctx context.Context, in *organizations.UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.UpdateRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.UpdateRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *organizations.UpdateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewOrganizationsClient creates a new instance of OrganizationsClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewOrganizationsClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *OrganizationsClient {
+	mock := &OrganizationsClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/OrganizationsServer.go b/proto/mocks/OrganizationsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..aa3ef3ad77177f56dd272bbcd19abacdd7e6a0a8
--- /dev/null
+++ b/proto/mocks/OrganizationsServer.go
@@ -0,0 +1,166 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	mock "github.com/stretchr/testify/mock"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	organizations "git.perx.ru/perxis/perxis-go/proto/organizations"
+)
+
+// OrganizationsServer is an autogenerated mock type for the OrganizationsServer type
+type OrganizationsServer struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: _a0, _a1
+func (_m *OrganizationsServer) Create(_a0 context.Context, _a1 *organizations.CreateRequest) (*organizations.CreateResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *organizations.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.CreateRequest) (*organizations.CreateResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.CreateRequest) *organizations.CreateResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*organizations.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *organizations.CreateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: _a0, _a1
+func (_m *OrganizationsServer) Delete(_a0 context.Context, _a1 *organizations.DeleteRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.DeleteRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.DeleteRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *organizations.DeleteRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Find provides a mock function with given fields: _a0, _a1
+func (_m *OrganizationsServer) Find(_a0 context.Context, _a1 *organizations.FindRequest) (*organizations.FindResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *organizations.FindResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.FindRequest) (*organizations.FindResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.FindRequest) *organizations.FindResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*organizations.FindResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *organizations.FindRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *OrganizationsServer) Get(_a0 context.Context, _a1 *organizations.GetRequest) (*organizations.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *organizations.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.GetRequest) (*organizations.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.GetRequest) *organizations.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*organizations.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *organizations.GetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: _a0, _a1
+func (_m *OrganizationsServer) Update(_a0 context.Context, _a1 *organizations.UpdateRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.UpdateRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *organizations.UpdateRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *organizations.UpdateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedOrganizationsServer provides a mock function with given fields:
+func (_m *OrganizationsServer) mustEmbedUnimplementedOrganizationsServer() {
+	_m.Called()
+}
+
+// NewOrganizationsServer creates a new instance of OrganizationsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewOrganizationsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *OrganizationsServer {
+	mock := &OrganizationsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/ReferencesClient.go b/proto/mocks/ReferencesClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..cc9a4344287be8e20da771edcb4869b474596f7f
--- /dev/null
+++ b/proto/mocks/ReferencesClient.go
@@ -0,0 +1,98 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	grpc "google.golang.org/grpc"
+
+	mock "github.com/stretchr/testify/mock"
+
+	references "git.perx.ru/perxis/perxis-go/proto/references"
+)
+
+// ReferencesClient is an autogenerated mock type for the ReferencesClient type
+type ReferencesClient struct {
+	mock.Mock
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *ReferencesClient) Get(ctx context.Context, in *references.GetRequest, opts ...grpc.CallOption) (*references.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *references.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *references.GetRequest, ...grpc.CallOption) (*references.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *references.GetRequest, ...grpc.CallOption) *references.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*references.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *references.GetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Publish provides a mock function with given fields: ctx, in, opts
+func (_m *ReferencesClient) Publish(ctx context.Context, in *references.PublishRequest, opts ...grpc.CallOption) (*references.PublishResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *references.PublishResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *references.PublishRequest, ...grpc.CallOption) (*references.PublishResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *references.PublishRequest, ...grpc.CallOption) *references.PublishResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*references.PublishResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *references.PublishRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewReferencesClient creates a new instance of ReferencesClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewReferencesClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *ReferencesClient {
+	mock := &ReferencesClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/ReferencesServer.go b/proto/mocks/ReferencesServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..14555790eb4fbc287347fbbe6695c45c5339426b
--- /dev/null
+++ b/proto/mocks/ReferencesServer.go
@@ -0,0 +1,86 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	references "git.perx.ru/perxis/perxis-go/proto/references"
+	mock "github.com/stretchr/testify/mock"
+)
+
+// ReferencesServer is an autogenerated mock type for the ReferencesServer type
+type ReferencesServer struct {
+	mock.Mock
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *ReferencesServer) Get(_a0 context.Context, _a1 *references.GetRequest) (*references.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *references.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *references.GetRequest) (*references.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *references.GetRequest) *references.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*references.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *references.GetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Publish provides a mock function with given fields: _a0, _a1
+func (_m *ReferencesServer) Publish(_a0 context.Context, _a1 *references.PublishRequest) (*references.PublishResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *references.PublishResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *references.PublishRequest) (*references.PublishResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *references.PublishRequest) *references.PublishResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*references.PublishResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *references.PublishRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedReferencesServer provides a mock function with given fields:
+func (_m *ReferencesServer) mustEmbedUnimplementedReferencesServer() {
+	_m.Called()
+}
+
+// NewReferencesServer creates a new instance of ReferencesServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewReferencesServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *ReferencesServer {
+	mock := &ReferencesServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/RolesClient.go b/proto/mocks/RolesClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..a70da1481947e96f722937fa1ecc969c8c475cc7
--- /dev/null
+++ b/proto/mocks/RolesClient.go
@@ -0,0 +1,198 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	grpc "google.golang.org/grpc"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+
+	roles "git.perx.ru/perxis/perxis-go/proto/roles"
+)
+
+// RolesClient is an autogenerated mock type for the RolesClient type
+type RolesClient struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: ctx, in, opts
+func (_m *RolesClient) Create(ctx context.Context, in *roles.CreateRequest, opts ...grpc.CallOption) (*roles.CreateResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *roles.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.CreateRequest, ...grpc.CallOption) (*roles.CreateResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.CreateRequest, ...grpc.CallOption) *roles.CreateResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*roles.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *roles.CreateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, in, opts
+func (_m *RolesClient) Delete(ctx context.Context, in *roles.DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.DeleteRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.DeleteRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *roles.DeleteRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *RolesClient) Get(ctx context.Context, in *roles.GetRequest, opts ...grpc.CallOption) (*roles.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *roles.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.GetRequest, ...grpc.CallOption) (*roles.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.GetRequest, ...grpc.CallOption) *roles.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*roles.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *roles.GetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: ctx, in, opts
+func (_m *RolesClient) List(ctx context.Context, in *roles.ListRequest, opts ...grpc.CallOption) (*roles.ListResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *roles.ListResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.ListRequest, ...grpc.CallOption) (*roles.ListResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.ListRequest, ...grpc.CallOption) *roles.ListResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*roles.ListResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *roles.ListRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: ctx, in, opts
+func (_m *RolesClient) Update(ctx context.Context, in *roles.UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.UpdateRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.UpdateRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *roles.UpdateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewRolesClient creates a new instance of RolesClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewRolesClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *RolesClient {
+	mock := &RolesClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/RolesServer.go b/proto/mocks/RolesServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..7a9119863bde4807b2dc47a5ad0e6c35521acef2
--- /dev/null
+++ b/proto/mocks/RolesServer.go
@@ -0,0 +1,166 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	mock "github.com/stretchr/testify/mock"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	roles "git.perx.ru/perxis/perxis-go/proto/roles"
+)
+
+// RolesServer is an autogenerated mock type for the RolesServer type
+type RolesServer struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: _a0, _a1
+func (_m *RolesServer) Create(_a0 context.Context, _a1 *roles.CreateRequest) (*roles.CreateResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *roles.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.CreateRequest) (*roles.CreateResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.CreateRequest) *roles.CreateResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*roles.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *roles.CreateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: _a0, _a1
+func (_m *RolesServer) Delete(_a0 context.Context, _a1 *roles.DeleteRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.DeleteRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.DeleteRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *roles.DeleteRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *RolesServer) Get(_a0 context.Context, _a1 *roles.GetRequest) (*roles.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *roles.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.GetRequest) (*roles.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.GetRequest) *roles.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*roles.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *roles.GetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: _a0, _a1
+func (_m *RolesServer) List(_a0 context.Context, _a1 *roles.ListRequest) (*roles.ListResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *roles.ListResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.ListRequest) (*roles.ListResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.ListRequest) *roles.ListResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*roles.ListResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *roles.ListRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: _a0, _a1
+func (_m *RolesServer) Update(_a0 context.Context, _a1 *roles.UpdateRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.UpdateRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *roles.UpdateRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *roles.UpdateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedRolesServer provides a mock function with given fields:
+func (_m *RolesServer) mustEmbedUnimplementedRolesServer() {
+	_m.Called()
+}
+
+// NewRolesServer creates a new instance of RolesServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewRolesServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *RolesServer {
+	mock := &RolesServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/SpacesClient.go b/proto/mocks/SpacesClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..c73344dfd7c46cde2cbddea687129ff0c0f78bae
--- /dev/null
+++ b/proto/mocks/SpacesClient.go
@@ -0,0 +1,363 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	grpc "google.golang.org/grpc"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+
+	spaces "git.perx.ru/perxis/perxis-go/proto/spaces"
+)
+
+// SpacesClient is an autogenerated mock type for the SpacesClient type
+type SpacesClient struct {
+	mock.Mock
+}
+
+// AbortTransfer provides a mock function with given fields: ctx, in, opts
+func (_m *SpacesClient) AbortTransfer(ctx context.Context, in *spaces.AbortTransferRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.AbortTransferRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.AbortTransferRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.AbortTransferRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Create provides a mock function with given fields: ctx, in, opts
+func (_m *SpacesClient) Create(ctx context.Context, in *spaces.CreateRequest, opts ...grpc.CallOption) (*spaces.CreateResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *spaces.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.CreateRequest, ...grpc.CallOption) (*spaces.CreateResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.CreateRequest, ...grpc.CallOption) *spaces.CreateResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*spaces.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.CreateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, in, opts
+func (_m *SpacesClient) Delete(ctx context.Context, in *spaces.DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.DeleteRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.DeleteRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.DeleteRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *SpacesClient) Get(ctx context.Context, in *spaces.GetRequest, opts ...grpc.CallOption) (*spaces.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *spaces.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.GetRequest, ...grpc.CallOption) (*spaces.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.GetRequest, ...grpc.CallOption) *spaces.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*spaces.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.GetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: ctx, in, opts
+func (_m *SpacesClient) List(ctx context.Context, in *spaces.ListRequest, opts ...grpc.CallOption) (*spaces.ListResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *spaces.ListResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.ListRequest, ...grpc.CallOption) (*spaces.ListResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.ListRequest, ...grpc.CallOption) *spaces.ListResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*spaces.ListResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.ListRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListTransfers provides a mock function with given fields: ctx, in, opts
+func (_m *SpacesClient) ListTransfers(ctx context.Context, in *spaces.ListTransfersRequest, opts ...grpc.CallOption) (*spaces.ListTransfersResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *spaces.ListTransfersResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.ListTransfersRequest, ...grpc.CallOption) (*spaces.ListTransfersResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.ListTransfersRequest, ...grpc.CallOption) *spaces.ListTransfersResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*spaces.ListTransfersResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.ListTransfersRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Move provides a mock function with given fields: ctx, in, opts
+func (_m *SpacesClient) Move(ctx context.Context, in *spaces.MoveRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.MoveRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.MoveRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.MoveRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Transfer provides a mock function with given fields: ctx, in, opts
+func (_m *SpacesClient) Transfer(ctx context.Context, in *spaces.TransferRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.TransferRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.TransferRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.TransferRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: ctx, in, opts
+func (_m *SpacesClient) Update(ctx context.Context, in *spaces.UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.UpdateRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.UpdateRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.UpdateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdateConfig provides a mock function with given fields: ctx, in, opts
+func (_m *SpacesClient) UpdateConfig(ctx context.Context, in *spaces.UpdateConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.UpdateConfigRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.UpdateConfigRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.UpdateConfigRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewSpacesClient creates a new instance of SpacesClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewSpacesClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *SpacesClient {
+	mock := &SpacesClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/SpacesServer.go b/proto/mocks/SpacesServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..410c1651174b63a1555f43a38ce91872b922aa76
--- /dev/null
+++ b/proto/mocks/SpacesServer.go
@@ -0,0 +1,296 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	mock "github.com/stretchr/testify/mock"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	spaces "git.perx.ru/perxis/perxis-go/proto/spaces"
+)
+
+// SpacesServer is an autogenerated mock type for the SpacesServer type
+type SpacesServer struct {
+	mock.Mock
+}
+
+// AbortTransfer provides a mock function with given fields: _a0, _a1
+func (_m *SpacesServer) AbortTransfer(_a0 context.Context, _a1 *spaces.AbortTransferRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.AbortTransferRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.AbortTransferRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.AbortTransferRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Create provides a mock function with given fields: _a0, _a1
+func (_m *SpacesServer) Create(_a0 context.Context, _a1 *spaces.CreateRequest) (*spaces.CreateResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *spaces.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.CreateRequest) (*spaces.CreateResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.CreateRequest) *spaces.CreateResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*spaces.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.CreateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: _a0, _a1
+func (_m *SpacesServer) Delete(_a0 context.Context, _a1 *spaces.DeleteRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.DeleteRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.DeleteRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.DeleteRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *SpacesServer) Get(_a0 context.Context, _a1 *spaces.GetRequest) (*spaces.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *spaces.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.GetRequest) (*spaces.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.GetRequest) *spaces.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*spaces.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.GetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// List provides a mock function with given fields: _a0, _a1
+func (_m *SpacesServer) List(_a0 context.Context, _a1 *spaces.ListRequest) (*spaces.ListResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *spaces.ListResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.ListRequest) (*spaces.ListResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.ListRequest) *spaces.ListResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*spaces.ListResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.ListRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// ListTransfers provides a mock function with given fields: _a0, _a1
+func (_m *SpacesServer) ListTransfers(_a0 context.Context, _a1 *spaces.ListTransfersRequest) (*spaces.ListTransfersResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *spaces.ListTransfersResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.ListTransfersRequest) (*spaces.ListTransfersResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.ListTransfersRequest) *spaces.ListTransfersResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*spaces.ListTransfersResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.ListTransfersRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Move provides a mock function with given fields: _a0, _a1
+func (_m *SpacesServer) Move(_a0 context.Context, _a1 *spaces.MoveRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.MoveRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.MoveRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.MoveRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Transfer provides a mock function with given fields: _a0, _a1
+func (_m *SpacesServer) Transfer(_a0 context.Context, _a1 *spaces.TransferRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.TransferRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.TransferRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.TransferRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: _a0, _a1
+func (_m *SpacesServer) Update(_a0 context.Context, _a1 *spaces.UpdateRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.UpdateRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.UpdateRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.UpdateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// UpdateConfig provides a mock function with given fields: _a0, _a1
+func (_m *SpacesServer) UpdateConfig(_a0 context.Context, _a1 *spaces.UpdateConfigRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.UpdateConfigRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *spaces.UpdateConfigRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *spaces.UpdateConfigRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedSpacesServer provides a mock function with given fields:
+func (_m *SpacesServer) mustEmbedUnimplementedSpacesServer() {
+	_m.Called()
+}
+
+// NewSpacesServer creates a new instance of SpacesServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewSpacesServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *SpacesServer {
+	mock := &SpacesServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeClientsServer.go b/proto/mocks/UnsafeClientsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..edaae9de67b00ad2de5e73568457380a3942a0cf
--- /dev/null
+++ b/proto/mocks/UnsafeClientsServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeClientsServer is an autogenerated mock type for the UnsafeClientsServer type
+type UnsafeClientsServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedClientsServer provides a mock function with given fields:
+func (_m *UnsafeClientsServer) mustEmbedUnimplementedClientsServer() {
+	_m.Called()
+}
+
+// NewUnsafeClientsServer creates a new instance of UnsafeClientsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeClientsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeClientsServer {
+	mock := &UnsafeClientsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeCollaboratorsServer.go b/proto/mocks/UnsafeCollaboratorsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..032a8659ddecf0af1f06afb59759bd5356d47df8
--- /dev/null
+++ b/proto/mocks/UnsafeCollaboratorsServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeCollaboratorsServer is an autogenerated mock type for the UnsafeCollaboratorsServer type
+type UnsafeCollaboratorsServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedCollaboratorsServer provides a mock function with given fields:
+func (_m *UnsafeCollaboratorsServer) mustEmbedUnimplementedCollaboratorsServer() {
+	_m.Called()
+}
+
+// NewUnsafeCollaboratorsServer creates a new instance of UnsafeCollaboratorsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeCollaboratorsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeCollaboratorsServer {
+	mock := &UnsafeCollaboratorsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeCollectionsServer.go b/proto/mocks/UnsafeCollectionsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..f827fa421970a92974109459487cbac4b2692524
--- /dev/null
+++ b/proto/mocks/UnsafeCollectionsServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeCollectionsServer is an autogenerated mock type for the UnsafeCollectionsServer type
+type UnsafeCollectionsServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedCollectionsServer provides a mock function with given fields:
+func (_m *UnsafeCollectionsServer) mustEmbedUnimplementedCollectionsServer() {
+	_m.Called()
+}
+
+// NewUnsafeCollectionsServer creates a new instance of UnsafeCollectionsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeCollectionsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeCollectionsServer {
+	mock := &UnsafeCollectionsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeDeliveryServer.go b/proto/mocks/UnsafeDeliveryServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..ad857a41f102912e0ce115145900b0541286e4cd
--- /dev/null
+++ b/proto/mocks/UnsafeDeliveryServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeDeliveryServer is an autogenerated mock type for the UnsafeDeliveryServer type
+type UnsafeDeliveryServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedDeliveryServer provides a mock function with given fields:
+func (_m *UnsafeDeliveryServer) mustEmbedUnimplementedDeliveryServer() {
+	_m.Called()
+}
+
+// NewUnsafeDeliveryServer creates a new instance of UnsafeDeliveryServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeDeliveryServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeDeliveryServer {
+	mock := &UnsafeDeliveryServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeEnvironmentsServer.go b/proto/mocks/UnsafeEnvironmentsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..c6499e683c1e940b868c8b2a45e3a5a28d72240f
--- /dev/null
+++ b/proto/mocks/UnsafeEnvironmentsServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeEnvironmentsServer is an autogenerated mock type for the UnsafeEnvironmentsServer type
+type UnsafeEnvironmentsServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedEnvironmentsServer provides a mock function with given fields:
+func (_m *UnsafeEnvironmentsServer) mustEmbedUnimplementedEnvironmentsServer() {
+	_m.Called()
+}
+
+// NewUnsafeEnvironmentsServer creates a new instance of UnsafeEnvironmentsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeEnvironmentsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeEnvironmentsServer {
+	mock := &UnsafeEnvironmentsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeFilesServer.go b/proto/mocks/UnsafeFilesServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..4b952359a53fcf45b88f015ae53aebbb1273d0a6
--- /dev/null
+++ b/proto/mocks/UnsafeFilesServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeFilesServer is an autogenerated mock type for the UnsafeFilesServer type
+type UnsafeFilesServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedFilesServer provides a mock function with given fields:
+func (_m *UnsafeFilesServer) mustEmbedUnimplementedFilesServer() {
+	_m.Called()
+}
+
+// NewUnsafeFilesServer creates a new instance of UnsafeFilesServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeFilesServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeFilesServer {
+	mock := &UnsafeFilesServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeImagesServer.go b/proto/mocks/UnsafeImagesServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..b99df04a247a0772ddb4e22624045539e5ee65c9
--- /dev/null
+++ b/proto/mocks/UnsafeImagesServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeImagesServer is an autogenerated mock type for the UnsafeImagesServer type
+type UnsafeImagesServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedImagesServer provides a mock function with given fields:
+func (_m *UnsafeImagesServer) mustEmbedUnimplementedImagesServer() {
+	_m.Called()
+}
+
+// NewUnsafeImagesServer creates a new instance of UnsafeImagesServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeImagesServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeImagesServer {
+	mock := &UnsafeImagesServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeInvitationsServer.go b/proto/mocks/UnsafeInvitationsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..f7cfa3550e3b274aa92c2de1757973991e40a8f6
--- /dev/null
+++ b/proto/mocks/UnsafeInvitationsServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeInvitationsServer is an autogenerated mock type for the UnsafeInvitationsServer type
+type UnsafeInvitationsServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedInvitationsServer provides a mock function with given fields:
+func (_m *UnsafeInvitationsServer) mustEmbedUnimplementedInvitationsServer() {
+	_m.Called()
+}
+
+// NewUnsafeInvitationsServer creates a new instance of UnsafeInvitationsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeInvitationsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeInvitationsServer {
+	mock := &UnsafeInvitationsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeItemsServer.go b/proto/mocks/UnsafeItemsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..a261bf4ab6d04bf02baf6a82d1abb2ac9d92cc0a
--- /dev/null
+++ b/proto/mocks/UnsafeItemsServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeItemsServer is an autogenerated mock type for the UnsafeItemsServer type
+type UnsafeItemsServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedItemsServer provides a mock function with given fields:
+func (_m *UnsafeItemsServer) mustEmbedUnimplementedItemsServer() {
+	_m.Called()
+}
+
+// NewUnsafeItemsServer creates a new instance of UnsafeItemsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeItemsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeItemsServer {
+	mock := &UnsafeItemsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeLocalesServer.go b/proto/mocks/UnsafeLocalesServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..e66f9ac5cc32336dd6d11db3380c1469a5bc4893
--- /dev/null
+++ b/proto/mocks/UnsafeLocalesServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeLocalesServer is an autogenerated mock type for the UnsafeLocalesServer type
+type UnsafeLocalesServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedLocalesServer provides a mock function with given fields:
+func (_m *UnsafeLocalesServer) mustEmbedUnimplementedLocalesServer() {
+	_m.Called()
+}
+
+// NewUnsafeLocalesServer creates a new instance of UnsafeLocalesServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeLocalesServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeLocalesServer {
+	mock := &UnsafeLocalesServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeMembersServer.go b/proto/mocks/UnsafeMembersServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..14df5465251661b5257fb5ea56ff085f027e0639
--- /dev/null
+++ b/proto/mocks/UnsafeMembersServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeMembersServer is an autogenerated mock type for the UnsafeMembersServer type
+type UnsafeMembersServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedMembersServer provides a mock function with given fields:
+func (_m *UnsafeMembersServer) mustEmbedUnimplementedMembersServer() {
+	_m.Called()
+}
+
+// NewUnsafeMembersServer creates a new instance of UnsafeMembersServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeMembersServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeMembersServer {
+	mock := &UnsafeMembersServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeObserverServer.go b/proto/mocks/UnsafeObserverServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..8c68681a2d0428ce19a25378fd27910707a870ed
--- /dev/null
+++ b/proto/mocks/UnsafeObserverServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeObserverServer is an autogenerated mock type for the UnsafeObserverServer type
+type UnsafeObserverServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedObserverServer provides a mock function with given fields:
+func (_m *UnsafeObserverServer) mustEmbedUnimplementedObserverServer() {
+	_m.Called()
+}
+
+// NewUnsafeObserverServer creates a new instance of UnsafeObserverServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeObserverServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeObserverServer {
+	mock := &UnsafeObserverServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeOperationServiceServer.go b/proto/mocks/UnsafeOperationServiceServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..46d3cdaa3f846a56633072495e607e4d9e8e23c8
--- /dev/null
+++ b/proto/mocks/UnsafeOperationServiceServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeOperationServiceServer is an autogenerated mock type for the UnsafeOperationServiceServer type
+type UnsafeOperationServiceServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedOperationServiceServer provides a mock function with given fields:
+func (_m *UnsafeOperationServiceServer) mustEmbedUnimplementedOperationServiceServer() {
+	_m.Called()
+}
+
+// NewUnsafeOperationServiceServer creates a new instance of UnsafeOperationServiceServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeOperationServiceServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeOperationServiceServer {
+	mock := &UnsafeOperationServiceServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeOrganizationsServer.go b/proto/mocks/UnsafeOrganizationsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..378edd0eb09d9556bb1d667bb4d99d162e4da8a0
--- /dev/null
+++ b/proto/mocks/UnsafeOrganizationsServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeOrganizationsServer is an autogenerated mock type for the UnsafeOrganizationsServer type
+type UnsafeOrganizationsServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedOrganizationsServer provides a mock function with given fields:
+func (_m *UnsafeOrganizationsServer) mustEmbedUnimplementedOrganizationsServer() {
+	_m.Called()
+}
+
+// NewUnsafeOrganizationsServer creates a new instance of UnsafeOrganizationsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeOrganizationsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeOrganizationsServer {
+	mock := &UnsafeOrganizationsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeReferencesServer.go b/proto/mocks/UnsafeReferencesServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..f0ba11eb665bb280e79ee26f39328fcc5f8df6f6
--- /dev/null
+++ b/proto/mocks/UnsafeReferencesServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeReferencesServer is an autogenerated mock type for the UnsafeReferencesServer type
+type UnsafeReferencesServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedReferencesServer provides a mock function with given fields:
+func (_m *UnsafeReferencesServer) mustEmbedUnimplementedReferencesServer() {
+	_m.Called()
+}
+
+// NewUnsafeReferencesServer creates a new instance of UnsafeReferencesServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeReferencesServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeReferencesServer {
+	mock := &UnsafeReferencesServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeRolesServer.go b/proto/mocks/UnsafeRolesServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..5ac0028d3c1ce25b36ae39db5bf3bdeab3d6d411
--- /dev/null
+++ b/proto/mocks/UnsafeRolesServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeRolesServer is an autogenerated mock type for the UnsafeRolesServer type
+type UnsafeRolesServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedRolesServer provides a mock function with given fields:
+func (_m *UnsafeRolesServer) mustEmbedUnimplementedRolesServer() {
+	_m.Called()
+}
+
+// NewUnsafeRolesServer creates a new instance of UnsafeRolesServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeRolesServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeRolesServer {
+	mock := &UnsafeRolesServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeSpacesServer.go b/proto/mocks/UnsafeSpacesServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..3985b213a578bbdba843c55ea9d417742d28397a
--- /dev/null
+++ b/proto/mocks/UnsafeSpacesServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeSpacesServer is an autogenerated mock type for the UnsafeSpacesServer type
+type UnsafeSpacesServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedSpacesServer provides a mock function with given fields:
+func (_m *UnsafeSpacesServer) mustEmbedUnimplementedSpacesServer() {
+	_m.Called()
+}
+
+// NewUnsafeSpacesServer creates a new instance of UnsafeSpacesServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeSpacesServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeSpacesServer {
+	mock := &UnsafeSpacesServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeUsersServer.go b/proto/mocks/UnsafeUsersServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..6fb87834b8833d591be1472515baacf05e53817b
--- /dev/null
+++ b/proto/mocks/UnsafeUsersServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeUsersServer is an autogenerated mock type for the UnsafeUsersServer type
+type UnsafeUsersServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedUsersServer provides a mock function with given fields:
+func (_m *UnsafeUsersServer) mustEmbedUnimplementedUsersServer() {
+	_m.Called()
+}
+
+// NewUnsafeUsersServer creates a new instance of UnsafeUsersServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeUsersServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeUsersServer {
+	mock := &UnsafeUsersServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UnsafeVersionsServer.go b/proto/mocks/UnsafeVersionsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..551623161886a3f4f852c90a57dc8a4027dbf401
--- /dev/null
+++ b/proto/mocks/UnsafeVersionsServer.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// UnsafeVersionsServer is an autogenerated mock type for the UnsafeVersionsServer type
+type UnsafeVersionsServer struct {
+	mock.Mock
+}
+
+// mustEmbedUnimplementedVersionsServer provides a mock function with given fields:
+func (_m *UnsafeVersionsServer) mustEmbedUnimplementedVersionsServer() {
+	_m.Called()
+}
+
+// NewUnsafeVersionsServer creates a new instance of UnsafeVersionsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUnsafeVersionsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UnsafeVersionsServer {
+	mock := &UnsafeVersionsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UsersClient.go b/proto/mocks/UsersClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..3bc7707535e4644f66f4105fbe01ae2374de3274
--- /dev/null
+++ b/proto/mocks/UsersClient.go
@@ -0,0 +1,231 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	grpc "google.golang.org/grpc"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+
+	users "git.perx.ru/perxis/perxis-go/proto/users"
+)
+
+// UsersClient is an autogenerated mock type for the UsersClient type
+type UsersClient struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: ctx, in, opts
+func (_m *UsersClient) Create(ctx context.Context, in *users.CreateRequest, opts ...grpc.CallOption) (*users.CreateResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *users.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.CreateRequest, ...grpc.CallOption) (*users.CreateResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *users.CreateRequest, ...grpc.CallOption) *users.CreateResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*users.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *users.CreateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: ctx, in, opts
+func (_m *UsersClient) Delete(ctx context.Context, in *users.DeleteRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.DeleteRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *users.DeleteRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *users.DeleteRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Find provides a mock function with given fields: ctx, in, opts
+func (_m *UsersClient) Find(ctx context.Context, in *users.FindRequest, opts ...grpc.CallOption) (*users.FindResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *users.FindResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.FindRequest, ...grpc.CallOption) (*users.FindResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *users.FindRequest, ...grpc.CallOption) *users.FindResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*users.FindResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *users.FindRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *UsersClient) Get(ctx context.Context, in *users.GetRequest, opts ...grpc.CallOption) (*users.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *users.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.GetRequest, ...grpc.CallOption) (*users.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *users.GetRequest, ...grpc.CallOption) *users.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*users.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *users.GetRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetByIdentity provides a mock function with given fields: ctx, in, opts
+func (_m *UsersClient) GetByIdentity(ctx context.Context, in *users.GetByIdentityRequest, opts ...grpc.CallOption) (*users.GetByIdentityResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *users.GetByIdentityResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.GetByIdentityRequest, ...grpc.CallOption) (*users.GetByIdentityResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *users.GetByIdentityRequest, ...grpc.CallOption) *users.GetByIdentityResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*users.GetByIdentityResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *users.GetByIdentityRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: ctx, in, opts
+func (_m *UsersClient) Update(ctx context.Context, in *users.UpdateRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.UpdateRequest, ...grpc.CallOption) (*emptypb.Empty, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *users.UpdateRequest, ...grpc.CallOption) *emptypb.Empty); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *users.UpdateRequest, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewUsersClient creates a new instance of UsersClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUsersClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UsersClient {
+	mock := &UsersClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/UsersServer.go b/proto/mocks/UsersServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..837d164578df39775f03cb3845cdbe57f2f11bb6
--- /dev/null
+++ b/proto/mocks/UsersServer.go
@@ -0,0 +1,192 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	mock "github.com/stretchr/testify/mock"
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	users "git.perx.ru/perxis/perxis-go/proto/users"
+)
+
+// UsersServer is an autogenerated mock type for the UsersServer type
+type UsersServer struct {
+	mock.Mock
+}
+
+// Create provides a mock function with given fields: _a0, _a1
+func (_m *UsersServer) Create(_a0 context.Context, _a1 *users.CreateRequest) (*users.CreateResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *users.CreateResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.CreateRequest) (*users.CreateResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *users.CreateRequest) *users.CreateResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*users.CreateResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *users.CreateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Delete provides a mock function with given fields: _a0, _a1
+func (_m *UsersServer) Delete(_a0 context.Context, _a1 *users.DeleteRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.DeleteRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *users.DeleteRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *users.DeleteRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Find provides a mock function with given fields: _a0, _a1
+func (_m *UsersServer) Find(_a0 context.Context, _a1 *users.FindRequest) (*users.FindResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *users.FindResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.FindRequest) (*users.FindResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *users.FindRequest) *users.FindResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*users.FindResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *users.FindRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *UsersServer) Get(_a0 context.Context, _a1 *users.GetRequest) (*users.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *users.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.GetRequest) (*users.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *users.GetRequest) *users.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*users.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *users.GetRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// GetByIdentity provides a mock function with given fields: _a0, _a1
+func (_m *UsersServer) GetByIdentity(_a0 context.Context, _a1 *users.GetByIdentityRequest) (*users.GetByIdentityResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *users.GetByIdentityResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.GetByIdentityRequest) (*users.GetByIdentityResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *users.GetByIdentityRequest) *users.GetByIdentityResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*users.GetByIdentityResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *users.GetByIdentityRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// Update provides a mock function with given fields: _a0, _a1
+func (_m *UsersServer) Update(_a0 context.Context, _a1 *users.UpdateRequest) (*emptypb.Empty, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *emptypb.Empty
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *users.UpdateRequest) (*emptypb.Empty, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *users.UpdateRequest) *emptypb.Empty); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*emptypb.Empty)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *users.UpdateRequest) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedUsersServer provides a mock function with given fields:
+func (_m *UsersServer) mustEmbedUnimplementedUsersServer() {
+	_m.Called()
+}
+
+// NewUsersServer creates a new instance of UsersServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewUsersServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *UsersServer {
+	mock := &UsersServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/VersionsClient.go b/proto/mocks/VersionsClient.go
new file mode 100644
index 0000000000000000000000000000000000000000..587b1cf8bf8c5e365fe3726800374c5bd042605a
--- /dev/null
+++ b/proto/mocks/VersionsClient.go
@@ -0,0 +1,67 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	content "git.perx.ru/perxis/perxis-go/proto/versions/content"
+
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	grpc "google.golang.org/grpc"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// VersionsClient is an autogenerated mock type for the VersionsClient type
+type VersionsClient struct {
+	mock.Mock
+}
+
+// Get provides a mock function with given fields: ctx, in, opts
+func (_m *VersionsClient) Get(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*content.GetResponse, error) {
+	_va := make([]interface{}, len(opts))
+	for _i := range opts {
+		_va[_i] = opts[_i]
+	}
+	var _ca []interface{}
+	_ca = append(_ca, ctx, in)
+	_ca = append(_ca, _va...)
+	ret := _m.Called(_ca...)
+
+	var r0 *content.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *emptypb.Empty, ...grpc.CallOption) (*content.GetResponse, error)); ok {
+		return rf(ctx, in, opts...)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *emptypb.Empty, ...grpc.CallOption) *content.GetResponse); ok {
+		r0 = rf(ctx, in, opts...)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*content.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *emptypb.Empty, ...grpc.CallOption) error); ok {
+		r1 = rf(ctx, in, opts...)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// NewVersionsClient creates a new instance of VersionsClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewVersionsClient(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *VersionsClient {
+	mock := &VersionsClient{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/VersionsServer.go b/proto/mocks/VersionsServer.go
new file mode 100644
index 0000000000000000000000000000000000000000..4509e2c4d3bc39ac42d12a91cc709b1f973a5c90
--- /dev/null
+++ b/proto/mocks/VersionsServer.go
@@ -0,0 +1,63 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import (
+	context "context"
+
+	content "git.perx.ru/perxis/perxis-go/proto/versions/content"
+
+	emptypb "google.golang.org/protobuf/types/known/emptypb"
+
+	mock "github.com/stretchr/testify/mock"
+)
+
+// VersionsServer is an autogenerated mock type for the VersionsServer type
+type VersionsServer struct {
+	mock.Mock
+}
+
+// Get provides a mock function with given fields: _a0, _a1
+func (_m *VersionsServer) Get(_a0 context.Context, _a1 *emptypb.Empty) (*content.GetResponse, error) {
+	ret := _m.Called(_a0, _a1)
+
+	var r0 *content.GetResponse
+	var r1 error
+	if rf, ok := ret.Get(0).(func(context.Context, *emptypb.Empty) (*content.GetResponse, error)); ok {
+		return rf(_a0, _a1)
+	}
+	if rf, ok := ret.Get(0).(func(context.Context, *emptypb.Empty) *content.GetResponse); ok {
+		r0 = rf(_a0, _a1)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(*content.GetResponse)
+		}
+	}
+
+	if rf, ok := ret.Get(1).(func(context.Context, *emptypb.Empty) error); ok {
+		r1 = rf(_a0, _a1)
+	} else {
+		r1 = ret.Error(1)
+	}
+
+	return r0, r1
+}
+
+// mustEmbedUnimplementedVersionsServer provides a mock function with given fields:
+func (_m *VersionsServer) mustEmbedUnimplementedVersionsServer() {
+	_m.Called()
+}
+
+// NewVersionsServer creates a new instance of VersionsServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewVersionsServer(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *VersionsServer {
+	mock := &VersionsServer{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/mocks/isOperation_Result.go b/proto/mocks/isOperation_Result.go
new file mode 100644
index 0000000000000000000000000000000000000000..cc9c1717419e4b8d48f627947b360689af80841c
--- /dev/null
+++ b/proto/mocks/isOperation_Result.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.33.3. DO NOT EDIT.
+
+package mocks
+
+import mock "github.com/stretchr/testify/mock"
+
+// isOperation_Result is an autogenerated mock type for the isOperation_Result type
+type isOperation_Result struct {
+	mock.Mock
+}
+
+// isOperation_Result provides a mock function with given fields:
+func (_m *isOperation_Result) isOperation_Result() {
+	_m.Called()
+}
+
+// newIsOperation_Result creates a new instance of isOperation_Result. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func newIsOperation_Result(t interface {
+	mock.TestingT
+	Cleanup(func())
+}) *isOperation_Result {
+	mock := &isOperation_Result{}
+	mock.Mock.Test(t)
+
+	t.Cleanup(func() { mock.AssertExpectations(t) })
+
+	return mock
+}
diff --git a/proto/organizations/organizations.pb.go b/proto/organizations/organizations.pb.go
index fa19fd7b92e181a9bac136d55a0a9805cb68f3c0..b744640573280053557944e9cbd93a43b0be7b46 100644
--- a/proto/organizations/organizations.pb.go
+++ b/proto/organizations/organizations.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.29.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: organizations/organizations.proto
 
 package organizations
diff --git a/proto/organizations/organizations_grpc.pb.go b/proto/organizations/organizations_grpc.pb.go
index 5e49e478a36c39bc65d5f48b559c63f04dcbed1a..dc737ac9a2eb95cb6c977f537216942dfd4164d7 100644
--- a/proto/organizations/organizations_grpc.pb.go
+++ b/proto/organizations/organizations_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v3.21.12
+// - protoc             v4.24.3
 // source: organizations/organizations.proto
 
 package organizations
diff --git a/proto/references/references.pb.go b/proto/references/references.pb.go
index 3ed0a65b0a7aa010299850c3315ad745e939442b..64895f9fa4b2e475dbc9444ee9998fc3d4204699 100644
--- a/proto/references/references.pb.go
+++ b/proto/references/references.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.31.0
-// 	protoc        v4.23.4
+// 	protoc        v4.24.3
 // source: references/references.proto
 
 package references
diff --git a/proto/references/references_grpc.pb.go b/proto/references/references_grpc.pb.go
index 61a8dbdb68a115dbf67c8f6211227b58c7d363cf..750571cede9b697ed3392cd96941b2db54934fcb 100644
--- a/proto/references/references_grpc.pb.go
+++ b/proto/references/references_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v4.23.4
+// - protoc             v4.24.3
 // source: references/references.proto
 
 package references
@@ -31,14 +31,14 @@ type ReferencesClient interface {
 	Get(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error)
 	// Publish принимает список ссылок на записи и публикует их с учетом вложенных ссылок.
 	//
-	// - В случае если флаг `recursive` установлен в true, то будут рекурсивно опубликованы
-	//   все записи на которые ссылаются найденные записи.
-	// - Если флаг `recursive` установлен в false, то будут опубликованы записи
-	//   только из списка `references` в случае если они не содержат не опубликованных
-	//   ссылок. В противном случае возвращается список не опубликованных записей `unpublished`
-	//   которые требуют публикации. Список `published` при этом будет пустым. В случае если флаг
-	//   `force` установлен в true, то записи будут опубликованы даже если они содержат
-	//   не опубликованные ссылки.
+	//   - В случае если флаг `recursive` установлен в true, то будут рекурсивно опубликованы
+	//     все записи на которые ссылаются найденные записи.
+	//   - Если флаг `recursive` установлен в false, то будут опубликованы записи
+	//     только из списка `references` в случае если они не содержат не опубликованных
+	//     ссылок. В противном случае возвращается список не опубликованных записей `unpublished`
+	//     которые требуют публикации. Список `published` при этом будет пустым. В случае если флаг
+	//     `force` установлен в true, то записи будут опубликованы даже если они содержат
+	//     не опубликованные ссылки.
 	Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*PublishResponse, error)
 }
 
@@ -76,14 +76,14 @@ type ReferencesServer interface {
 	Get(context.Context, *GetRequest) (*GetResponse, error)
 	// Publish принимает список ссылок на записи и публикует их с учетом вложенных ссылок.
 	//
-	// - В случае если флаг `recursive` установлен в true, то будут рекурсивно опубликованы
-	//   все записи на которые ссылаются найденные записи.
-	// - Если флаг `recursive` установлен в false, то будут опубликованы записи
-	//   только из списка `references` в случае если они не содержат не опубликованных
-	//   ссылок. В противном случае возвращается список не опубликованных записей `unpublished`
-	//   которые требуют публикации. Список `published` при этом будет пустым. В случае если флаг
-	//   `force` установлен в true, то записи будут опубликованы даже если они содержат
-	//   не опубликованные ссылки.
+	//   - В случае если флаг `recursive` установлен в true, то будут рекурсивно опубликованы
+	//     все записи на которые ссылаются найденные записи.
+	//   - Если флаг `recursive` установлен в false, то будут опубликованы записи
+	//     только из списка `references` в случае если они не содержат не опубликованных
+	//     ссылок. В противном случае возвращается список не опубликованных записей `unpublished`
+	//     которые требуют публикации. Список `published` при этом будет пустым. В случае если флаг
+	//     `force` установлен в true, то записи будут опубликованы даже если они содержат
+	//     не опубликованные ссылки.
 	Publish(context.Context, *PublishRequest) (*PublishResponse, error)
 	mustEmbedUnimplementedReferencesServer()
 }
diff --git a/proto/roles/roles.pb.go b/proto/roles/roles.pb.go
index 56a202a4fbe3cec46e1b6221bceb1050b031d373..0d70814977a6b765b8377739bd3f43704b98ec02 100644
--- a/proto/roles/roles.pb.go
+++ b/proto/roles/roles.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.29.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: roles/roles.proto
 
 package roles
diff --git a/proto/roles/roles_grpc.pb.go b/proto/roles/roles_grpc.pb.go
index 596c13fb4a72989aa5b4334908fc45c5fa97fcc6..13000eb2e08429aa6fcc93d5e133d4e3012c05ff 100644
--- a/proto/roles/roles_grpc.pb.go
+++ b/proto/roles/roles_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v3.21.12
+// - protoc             v4.24.3
 // source: roles/roles.proto
 
 package roles
diff --git a/proto/spaces/spaces.pb.go b/proto/spaces/spaces.pb.go
index 6fb15d8af880c6a8cd39704cee97c860d210fbac..d5c57b12ed99209be6e6bfeb15e5627c7eb58b40 100644
--- a/proto/spaces/spaces.pb.go
+++ b/proto/spaces/spaces.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.31.0
-// 	protoc        v4.23.4
+// 	protoc        v4.24.3
 // source: spaces/spaces.proto
 
 package spaces
diff --git a/proto/spaces/spaces_grpc.pb.go b/proto/spaces/spaces_grpc.pb.go
index f62bfa6dc1670c60e95a2d98a1db13275f43a3fe..18aa43b307fd40ad17480082c2cc104dea50ed9c 100644
--- a/proto/spaces/spaces_grpc.pb.go
+++ b/proto/spaces/spaces_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v4.23.4
+// - protoc             v4.24.3
 // source: spaces/spaces.proto
 
 package spaces
diff --git a/proto/users/users.pb.go b/proto/users/users.pb.go
index 8ab0e5ce776aa5aade419ce8e8adc81ee8df5d18..18f49f1a3c2e75c911a5f783e9f008da8d9c4c79 100644
--- a/proto/users/users.pb.go
+++ b/proto/users/users.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.29.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: users/users.proto
 
 package users
diff --git a/proto/users/users_grpc.pb.go b/proto/users/users_grpc.pb.go
index 8229c3b7e1b390aacdcd127a71efa166c138a494..30a2b84431e53679e4b92f0f3db43ffc8d0e190c 100644
--- a/proto/users/users_grpc.pb.go
+++ b/proto/users/users_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v3.21.12
+// - protoc             v4.24.3
 // source: users/users.proto
 
 package users
diff --git a/proto/versions/account/versions.pb.go b/proto/versions/account/versions.pb.go
index c9e0a8db89275bd866540cd39cd3e8444642caa3..9db5b3d2864ba0de22a950db158d66c3f488a36c 100644
--- a/proto/versions/account/versions.pb.go
+++ b/proto/versions/account/versions.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.29.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: versions/account/versions.proto
 
 package account
diff --git a/proto/versions/account/versions_grpc.pb.go b/proto/versions/account/versions_grpc.pb.go
index 2d017713cdd8731f90283b3596d6c391dc7acd49..6d3fd60aff0ad0692c3a2ec698607ee4c59b9f97 100644
--- a/proto/versions/account/versions_grpc.pb.go
+++ b/proto/versions/account/versions_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v3.21.12
+// - protoc             v4.24.3
 // source: versions/account/versions.proto
 
 package account
diff --git a/proto/versions/content/versions.pb.go b/proto/versions/content/versions.pb.go
index ad5fdb6eb88f9898488b5875d51939edd55853b7..c399b1a29d2e793d7918eaa456670d4b2a6d02ce 100644
--- a/proto/versions/content/versions.pb.go
+++ b/proto/versions/content/versions.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.29.0
-// 	protoc        v3.21.12
+// 	protoc-gen-go v1.31.0
+// 	protoc        v4.24.3
 // source: versions/content/versions.proto
 
 package content
diff --git a/proto/versions/content/versions_grpc.pb.go b/proto/versions/content/versions_grpc.pb.go
index f106e46d75acdbf07e54757730a3e078b3159ef4..25198d3383a04d58c15a85d83673445386e347b2 100644
--- a/proto/versions/content/versions_grpc.pb.go
+++ b/proto/versions/content/versions_grpc.pb.go
@@ -1,7 +1,7 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
 // - protoc-gen-go-grpc v1.3.0
-// - protoc             v3.21.12
+// - protoc             v4.24.3
 // source: versions/content/versions.proto
 
 package content